
名域 ts {
    导出 函数 是外部模块名称相对的这个不要(模块名称: 文字): 真假 {
        // TypeScript 1.0 spec (April 2014): 11.2.1
        // An external module 名称 is 'relative' if the 首个 term is '.' or '..'.
        // Update: We also consider a 路径 like `C:\foo.ts` 'relative' because we do not search for it in `node_modules` or treat it as an ambient module.
        返回 路径是相对的(模块名称) || !!是根磁盘路径(模块名称);
    }

    导出 函数 排序和消除重复诊断信息(diagnostics: 只读数组_<诊断_>): 诊断_[] {
        返回 sortAndDeduplicate(diagnostics, 比较诊断)!;
    }

    导出 函数 更改影响模块解析(旧选项: 编译选项_, 新选项: 编译选项_): 真假 {
        返回 !旧选项 || (旧选项.系统 !== 新选项.系统) || (旧选项.架构 !== 新选项.架构) || !数组是相等的(旧选项.定义, 新选项.定义);
    }

    /**
     * Iterates through the 父节点 chain of a n and performs the callback on each 父节点 until the callback
     * returns a truthy value, then returns that value.
     * If no such value is found, it applies the callback until the 父节点 pointer is 未定 or the callback returns 'quit'
     * At that point findAncestor returns 未定.
     */
    导出 函数 查找祖先<T 扩展 节点_>(n: 节点_, callback: (元素: 节点_) => 元素 作为 T): T | 未定;
    导出 函数 查找祖先(n: 节点_, callback: (元素: 节点_) => 真假 | 未定 | 'quit'): 节点_ | 未定;
    导出 函数 查找祖先(n: 节点_, callback: (元素: 节点_) => 真假 | 未定 | 'quit'): 节点_ {
        判断 (n) {
            常量 结果 = callback(n);
            如果 (结果 === 'quit') {
                返回 未定!;
            }
            否则 如果 (结果) {
                返回 n;
            }
            n = n.父节点!;
        }
        返回 未定!;
    }

    导出 函数 创建符号表(符号组?: 只读数组_<符号_>): 符号表_ {
        常量 结果 = 创建词典<符号_>() 转为 符号表_;
        如果 (符号组) {
            循环 (常量 符号 属于 符号组) {
                结果.设置(符号.名称, 符号);
            }
        }
        返回 结果;
    }

    导出 函数 转为路径(文件名: 文字, 基路径: 文字, 获取规范文件名: (路径?: 文字) => 文字): 路径_ {
        常量 非规范路径 = 是根磁盘路径(文件名)
            ? 正规化路径(文件名)
            : 获取标准化绝对路径(文件名, 基路径);
        返回 <路径_>获取规范文件名(非规范路径);
    }

    导出 函数 cloneMap(map: 符号表_): 符号表_;
    导出 函数 cloneMap<T>(map: 词典_<T>): 词典_<T>;
    导出 函数 cloneMap<T>(map: 词典_<T>): 词典_<T>;
    导出 函数 cloneMap<T>(map: 词典_<T> | 词典_<T> | 符号表_): 词典_<T> | 词典_<T> | 符号表_ {
        常量 克隆的 = 创建词典<T>();
        复制条目(map 转为 词典_<T>, 克隆的);
        返回 克隆的;
    }

    导出 函数 cast<TOut 扩展 TIn, TIn = 任意>(value: TIn | 未定, test: (value: TIn) => value 作为 TOut): TOut | 未定 {
        如果 (value !== 未定 && test(value)) 返回 value;

        如果 (value && 类为 (value 转为 任意).种类 === 'number') {
            调试_.失败(`Invalid cast. The supplied ${调试_.显示语法标志(value 转为 任意 转为 节点_)} did not pass the test '${调试_.getFunctionName(test)}'.`);
        }
        否则 {
            调试_.失败(`Invalid cast. The supplied value did not pass the test '${调试_.getFunctionName(test)}'.`);
        }
    }


    导出 变量 本地化诊断信息: 词典族_<文字>;

    导出 函数 获取本地消息(消息: 诊断信息_) {
        返回 本地化诊断信息 && 本地化诊断信息[消息.键] || 消息.消息;
    }

    导出 函数 创建文件诊断信息(文件: 源文件_, 开始: 数字, 长度: 数字, 消息: 诊断信息_, ...实参: (文字 | 数字)[]): 诊断_;
    导出 函数 创建文件诊断信息(文件: 源文件_, 开始: 数字, 长度: 数字, 消息: 诊断信息_): 诊断_ {
        调试_.断言大于或等于(开始, 0);
        调试_.断言大于或等于(长度, 0);

        如果 (文件) {
            调试_.断言小于或等于(开始, 文件.文本.长度);
            调试_.断言小于或等于(开始 + 长度, 文件.文本.长度);
        }

        变量 文本 = 获取本地消息(消息);

        如果 (arguments.长度 > 4) {
            文本 = 格式化文字从参数(文本, arguments, 4);
        }

        返回 {
            文件,
            开始,
            _长度: 长度,

            消息文本: 文本,
            级别: 消息.级别,
            代码: 消息.代码,
        };
    }

    /* @internal */
    导出 函数 格式化消息(_dummy: 任意, 消息: 诊断信息_): 文字 {
        变量 文本 = 获取本地消息(消息);

        如果 (arguments.长度 > 2) {
            文本 = 格式化文字从参数(文本, arguments, 2);
        }

        返回 文本;
    }

    导出 函数 创建编译器诊断(消息: 诊断信息_, ...args: (文字 | 数字)[]): 诊断_;
    导出 函数 创建编译器诊断(消息: 诊断信息_): 诊断_ {
        变量 文本 = 获取本地消息(消息);

        如果 (arguments.长度 > 1) {
            文本 = 格式化文字从参数(文本, arguments, 1);
        }

        返回 {
            文件: 未定,
            开始: 未定,
            _长度: 未定,

            消息文本: 文本,
            级别: 消息.级别,
            代码: 消息.代码
        };
    }

    导出 函数 从消息链创建编译器诊断信息(消息链: 诊断信息链_): 诊断_ {
        返回 {
            文件: 未定,
            开始: 未定,
            _长度: 未定,

            代码: 消息链.代码,
            级别: 消息链.级别,
            消息文本: 消息链.下个 ? 消息链 : 消息链.消息文本
        };
    }

    导出 函数 诊断消息链(详述: 诊断信息链_, 消息: 诊断信息_, ...参数组: 文字[]): 诊断信息链_;
    导出 函数 诊断消息链(详述: 诊断信息链_, 消息: 诊断信息_): 诊断信息链_ {
        变量 文本 = 获取本地消息(消息);

        如果 (arguments.长度 > 2) {
            文本 = 格式化文字从参数(文本, arguments, 2);
        }

        返回 {
            消息文本: 文本,
            级别: 消息.级别,
            代码: 消息.代码,

            下个: 详述
        };
    }

    导出 函数 连接诊断信息消息链组(头部链: 诊断信息链_, 尾随链: 诊断信息链_): 诊断信息链_ {
        变量 第一个链 = 头部链;
        判断 (第一个链.下个) {
            第一个链 = 第一个链.下个;
        }

        第一个链.下个 = 尾随链;
        返回 头部链;
    }


    函数 获取诊断文件名称(诊断: 诊断_): 文字 {
        返回 诊断.文件 ? 诊断.文件.文件名 : 未定!;
    }

    导出 函数 比较诊断(d1: 诊断_, d2: 诊断_): 比较结果_ {
        返回 比较字符串区分大小写(获取诊断文件名称(d1), 获取诊断文件名称(d2)) ||
            比较值(d1.开始!, d2.开始!) ||
            比较值(d1._长度!, d2._长度!) ||
            比较值(d1.代码, d2.代码) ||
            比较消息文本(d1.消息文本, d2.消息文本) ||
            比较结果_.等于_;
    }


    函数 比较消息文本(text1: 文字 | 诊断信息链_ | 未定, text2: 文字 | 诊断信息链_ | 未定): 比较结果_ {
        判断 (text1 && text2) {
            // We still have both chains.
            常量 string1 = 是文字(text1) ? text1 : text1.消息文本;
            常量 string2 = 是文字(text2) ? text2 : text2.消息文本;

            常量 res = 比较字符串区分大小写(string1, string2);
            如果 (res) {
                返回 res;
            }

            text1 = 是文字(text1) ? 未定 : text1.下个;
            text2 = 是文字(text2) ? 未定 : text2.下个;
        }

        如果 (!text1 && !text2) {
            // if the chains are done, then these messages are the same.
            返回 比较结果_.等于_;
        }

        // We still have one chain remaining.  The shorter chain should come 首个.
        返回 text1 ? 比较结果_.大于_ : 比较结果_.小于_;
    }

    导出 函数 规范化斜杠(路径: 文字): 文字 {
        返回 路径.替换(/\\/g, '/');
    }

    /**
     * Returns length of 路径 root (i.e. length of '/', 'x:/', '//server/share/, 文件:///user/files')
     */
    导出 函数 获取根长度(路径: 文字): 数字 {
        如果 (路径.字符代码在(0) === 字符_.斜杠号_) {
            如果 (路径.字符代码在(1) !== 字符_.斜杠号_) 返回 1;
            常量 p1 = 路径.索引位于('/', 2);
            如果 (p1 < 0) 返回 2;
            常量 p2 = 路径.索引位于('/', p1 + 1);
            如果 (p2 < 0) 返回 p1 + 1;
            返回 p2 + 1;
        }
        如果 (路径.字符代码在(1) === 字符_.冒号_) {
            如果 (路径.字符代码在(2) === 字符_.斜杠号_ || 路径.字符代码在(2) === 字符_.反斜杠号_) 返回 3;
        }
        // Per RFC 1738 '文件' URI schema has the shape 文件://<host>/<路径>
        // if <host> is omitted then it is assumed that host value is 'localhost',
        // however 斜杠号_ after the omitted <host> is not removed.
        // 文件:///folder1/file1 - this is a correct URI
        // 文件://folder2/file2 - this is an incorrect URI
        如果 (路径.最后索引位于('文件:///', 0) === 0) {
            返回 '文件:///'.长度;
        }
        常量 idx = 路径.索引位于('://');
        如果 (idx !== -1) {
            返回 idx + '://'.长度;
        }
        返回 0;
    }

    /**
     * Internally, we represent paths as strings with '/' as the directory separator.
     * When we make system calls (eg: LanguageServiceHost.getDirectory()),
     * we expect the host to correctly handle paths in our specified format.
     */
    导出 常量 目录分隔符 = '/';
    常量 目录分隔符字符代码 = 字符_.斜杠号_!;
    函数 获取规范化片段(规范化斜杠路径: 文字, 根长度: 数字): 文字[] {
        常量 片段组 = 规范化斜杠路径.子文本(根长度).分割(目录分隔符);
        常量 规范化的: 文字[] = [];
        循环 (常量 片段 属于 片段组) {
            如果 (片段 !== '.') {
                如果 (片段 === '..' && 规范化的.长度 > 0 && 最后或未定义(规范化的) !== '..') {
                    规范化的.弹出();
                }
                否则 {
                    // A part may be an empty string (which is 'falsy') if the 路径 had consecutive slashes,
                    // e.g. '路径//文件.ts'.  Drop these before re-joining the parts.
                    如果 (片段) {
                        规范化的.压入(片段);
                    }
                }
            }
        }

        返回 规范化的;
    }

    导出 函数 正规化路径(路径: 文字): 文字 {
        返回 正规化路径和片段(路径).路径;
    }

    导出 函数 正规化路径和片段(路径: 文字): { 路径: 文字, 片段: 文字[] } {
        路径 = 规范化斜杠(路径);
        常量 根长度 = 获取根长度(路径);
        常量 根 = 路径.子文本(0, 根长度);
        常量 片段组 = 获取规范化片段(路径, 根长度);
        如果 (片段组.长度) {
            常量 连接片段 = 根 + 片段组.连结(目录分隔符);
            返回 { 路径: 路径以分隔符结尾(路径) ? 连接片段 + 目录分隔符 : 连接片段, 片段: 片段组 };
        }
        否则 {
            返回 { 路径: 根, 片段: 片段组 };
        }
    }

    /** A 路径 ending with '/' refers to a directory only, never a 文件. */
    导出 函数 路径以分隔符结尾(路径: 文字): 真假 {
        返回 路径.字符代码在(路径.长度 - 1) === 目录分隔符字符代码;
    }

    /**
     * Returns the 路径 except for its basename. Eg:
     *
     * /路径/to/文件.ext -> /路径/to
     */
    导出 函数 获取目录路径(路径: 路径_): 路径_;
    导出 函数 获取目录路径(路径: 文字): 文字;
    导出 函数 获取目录路径(路径: 文字): 文字 {
        返回 路径.子文本(0, 数学_.max(获取根长度(路径), 路径.最后索引位于(目录分隔符)));
    }

    导出 函数 isUrl(路径: 文字) {
        返回 路径 && !是根磁盘路径(路径) && 文本包含(路径, '://');
    }

    导出 函数 路径是相对的(路径: 文字): 真假 {
        返回 /^\.\.?($|[\\/])/.测试(路径);
    }

    导出 函数 获取输出脚本目标(编译选项: 编译选项_) {
        变量 系统级架构 = 编译选项.系统 + '_' + 编译选项.架构;
        假如 (系统级架构) {
            若是 'win_386':
                返回 脚本目标_.win_368;
            若是 'win_amd64':
                返回 脚本目标_.win_amd64;
            若是 'linux_386':
                返回 脚本目标_.linux_386;
            若是 'linux_amd64':
                返回 脚本目标_.linux_amd64;
            若是 'linux_arm':
                返回 脚本目标_.linux_arm;
            若是 'darwin_386':
                返回 脚本目标_.darwin_386;
            若是 'darwin_amd64':
                返回 脚本目标_.darwin_amd64;
            默认:
                返回 脚本目标_.win_amd64;
        }
    }

    导出 函数 具有零或一个星号字符(str: 文字): 真假 {
        变量 看到星号 = 为假;
        循环 (变量 i = 0; i < str.长度; i++) {
            如果 (str.字符代码在(i) === 字符_.星号_) {
                如果 (!看到星号) {
                    看到星号 = 为真;
                }
                否则 {
                    // have already seen 星号_
                    返回 为假;
                }
            }
        }
        返回 为真;
    }

    导出 函数 是根磁盘路径(路径: 文字) {
        返回 路径 && 获取根长度(路径) !== 0;
    }

    导出 函数 转换到相对路径(绝对或相对路径: 文字, 基路径: 文字, 获取规范文件名: (路径: 文字) => 文字): 文字 {
        返回 !是根磁盘路径(绝对或相对路径)!
            ? 绝对或相对路径!
            : 获取相对路径到目录或URL(基路径, 绝对或相对路径, 基路径, 获取规范文件名, /*isAbsolutePathAnUrl*/ 为假)!;
    }

    函数 规范化路径组件(路径: 文字, 根长度: 数字) {
        常量 规范化片段组 = 获取规范化片段(路径, 根长度);
        返回 [路径.子文本(0, 根长度)].连接(规范化片段组);
    }

    导出 函数 获取规范化路径组件(路径: 文字, 当前目录: 文字) {
        路径 = 规范化斜杠(路径);
        变量 根长度 = 获取根长度(路径);
        如果 (根长度 === 0) {
            // If the 路径 is not rooted it is relative to 当前 directory
            路径 = 合并路径(规范化斜杠(当前目录), 路径);
            根长度 = 获取根长度(路径);
        }

        返回 规范化路径组件(路径, 根长度);
    }

    导出 函数 获取标准化绝对路径(文件名: 文字, 当前目录: 文字) {
        返回 从路径组件获取规范化路径(获取规范化路径组件(文件名, 当前目录));
    }

    导出 函数 从路径组件获取规范化路径(路径组件: 只读数组_<文字>) {
        如果 (路径组件 && 路径组件.长度) {
            返回 路径组件[0] + 路径组件.分切(1).连结(目录分隔符);
        }
    }

    导出 函数 获取路径从路径组件组(路径组件: 只读数组_<文字>) {
        如果 (路径组件.长度 === 0) 返回 '';

        常量 root = 路径组件[0] && 确保尾部目录分隔符(路径组件[0]);
        返回 root + 路径组件.分切(1).连结(目录分隔符);
    }

    函数 获取Url的规范化路径组件(url: 文字) {
        // Get root length of http://www.website.com/folder1/folder2/
        // In this example the root is: http://www.website.com/
        // normalized 路径 components should be ['http://www.website.com/', 'folder1', 'folder2']

        常量 url长度 = url.长度;
        // Initial root length is http:// part
        变量 根长度 = url.索引位于('://') + '://'.长度;
        判断 (根长度 < url长度) {
            // Consume all immediate slashes in the protocol
            // eg.initial rootlength is just 文件:// but it needs to consume another '/' in 文件:///
            如果 (url.字符代码在(根长度) === 字符_.斜杠号_) {
                根长度++;
            }
            否则 {
                // non 斜杠号_ character means we continue proceeding to next component of root search
                跳出;
            }
        }

        // there are no parts after http:// just return 当前 string as the pathComponent
        如果 (根长度 === url长度) {
            返回 [url];
        }

        // Find the index of '/' after website.com so the root can be http://www.website.com/ (from existing http://)
        常量 下个分隔符的索引 = url.索引位于(目录分隔符, 根长度);
        如果 (下个分隔符的索引 !== -1) {
            // Found the '/' after the website.com so the root is length of http://www.website.com/
            // and get components after the root normally like any other folder components
            根长度 = 下个分隔符的索引 + 1;
            返回 规范化路径组件(url, 根长度);
        }
        否则 {
            // Can't find the host assume the rest of the string as component
            // but make sure we 追加 '/' to it as root is not joined using '/'
            // eg. if url passed in was http://website.com we want to use root as [http://website.com/]
            // so that other 路径 manipulations will be correct and it can be merged with relative paths correctly
            返回 [url + 目录分隔符];
        }
    }

    函数 获取规范化路径或Url组件(路径或Url: 文字, 当前目录: 文字) {
        如果 (isUrl(路径或Url)) {
            返回 获取Url的规范化路径组件(路径或Url);
        }
        否则 {
            返回 获取规范化路径组件(路径或Url, 当前目录);
        }
    }

    导出 函数 获取相对路径到目录或URL(目录路径或Url: 文字, 相对或绝对路径: 文字, 当前目录: 文字, 获取规范文件名称: 获取规范文件名称_, 是绝对路径Url: 真假) {
        常量 路径组件 = 获取规范化路径或Url组件(相对或绝对路径, 当前目录);
        常量 目录组件 = 获取规范化路径或Url组件(目录路径或Url, 当前目录);
        如果 (目录组件.长度 > 1 && 最后或未定义(目录组件) === '') {
            // If the directory 路径 given was of 类型 test/cases/ then we really need components of directory to be only till its 名称
            // that is ['test', 'cases', ''] needs to be actually ['test', 'cases']
            目录组件.弹出();
        }

        // Find the component that differs
        变量 连接开始索引: 数字;
        循环 (连接开始索引 = 0; 连接开始索引 < 路径组件.长度 && 连接开始索引 < 目录组件.长度; 连接开始索引++) {
            如果 (获取规范文件名称(目录组件[连接开始索引]) !== 获取规范文件名称(路径组件[连接开始索引])) {
                跳出;
            }
        }

        // Get the relative 路径
        如果 (连接开始索引) {
            变量 相对路径 = '';
            常量 相对路径组件 = 路径组件.分切(连接开始索引, 路径组件.长度);
            循环 (; 连接开始索引 < 目录组件.长度; 连接开始索引++) {
                如果 (目录组件[连接开始索引] !== '') {
                    相对路径 = 相对路径 + '..' + 目录分隔符;
                }
            }

            返回 相对路径 + 相对路径组件.连结(目录分隔符);
        }

        // Cant find the relative 路径, get the absolute 路径
        变量 绝对路径 = 从路径组件获取规范化路径(路径组件);
        如果 (是绝对路径Url && 是根磁盘路径(绝对路径!)) {
            绝对路径 = '文件:///' + 绝对路径;
        }

        返回 绝对路径;
    }

    导出 函数 获取基文件名称(路径: 文字) {
        如果 (路径 === 未定) {
            返回 未定;
        }
        常量 i = 路径.最后索引位于(目录分隔符);
        返回 i < 0 ? 路径 : 路径.子文字(i + 1);
    }

    导出 函数 合并路径(路径1: 文字, 路径2: 文字): 文字 {
        如果 (!(路径1 && 路径1.长度)) 返回 路径2;
        如果 (!(路径2 && 路径2.长度)) 返回 路径1;
        如果 (获取根长度(路径2) !== 0) 返回 路径2;
        如果 (路径1.字符在(路径1.长度 - 1) === 目录分隔符) 返回 路径1 + 路径2;
        返回 路径1 + 目录分隔符 + 路径2;
    }

    /**
     * Removes a trailing directory separator from a 路径.
     * @param 路径 The 路径.
     */
    导出 函数 删除尾部目录分隔符(路径: 路径_): 路径_;
    导出 函数 删除尾部目录分隔符(路径: 文字): 文字;
    导出 函数 删除尾部目录分隔符(路径: 文字) {
        如果 (路径.字符在(路径.长度 - 1) === 目录分隔符) {
            返回 路径.子文本(0, 路径.长度 - 1);
        }

        返回 路径;
    }

    /**
     * Adds a trailing directory separator to a 路径, if it does not already have one.
     * @param 路径 The 路径.
     */
    导出 函数 确保尾部目录分隔符(路径: 文字) {
        如果 (路径.字符在(路径.长度 - 1) !== 目录分隔符) {
            返回 路径 + 目录分隔符;
        }

        返回 路径;
    }

    导出 函数 比较路径组(a: 文字, b: 文字, 当前目录: 文字, 忽略大小写?: 真假) {
        如果 (a === b) 返回 比较结果_.等于_;
        如果 (a === 未定) 返回 比较结果_.小于_;
        如果 (b === 未定) 返回 比较结果_.大于_;
        a = 删除尾部目录分隔符(a);
        b = 删除尾部目录分隔符(b);
        常量 a组件组 = 获取规范化路径组件(a, 当前目录);
        常量 b组件组 = 获取规范化路径组件(b, 当前目录);
        常量 共享长度 = 数学_.min(a组件组.长度, b组件组.长度);
        常量 比较器 = 忽略大小写 ? 边距文字不区分大小写 : 比较字符串区分大小写;
        循环 (变量 i = 0; i < 共享长度; i++) {
            常量 结果 = 比较器(a组件组[i], b组件组[i]);
            如果 (结果 !== 比较结果_.等于_) {
                返回 结果;
            }
        }

        返回 比较值(a组件组.长度, b组件组.长度);
    }

    导出 函数 包含路径(父节点: 文字, 子路径: 文字, 当前目录: 文字, 忽略大小写?: 真假) {
        如果 (父节点 === 未定 || 子路径 === 未定) 返回 为假;
        如果 (父节点 === 子路径) 返回 为真;
        父节点 = 删除尾部目录分隔符(父节点);
        子路径 = 删除尾部目录分隔符(子路径);
        如果 (父节点 === 子路径) 返回 为真;
        常量 父组件组 = 获取规范化路径组件(父节点, 当前目录);
        常量 子组件组 = 获取规范化路径组件(子路径, 当前目录);
        如果 (子组件组.长度 < 父组件组.长度) {
            返回 为假;
        }

        常量 比较器 = 忽略大小写 ? 相等文字不区分大小写 : 相等文字区分大小写;
        循环 (变量 i = 0; i < 父组件组.长度; i++) {
            如果 (!比较器(父组件组[i], 子组件组[i])) {
                返回 为假;
            }
        }

        返回 为真;
    }

    导出 函数 具有扩展(文件名: 文字): 真假 {
        返回 文本包含(获取基文件名称(文件名)!, '.');
    }

    导出 函数 文件扩展是(路径: 文字, 扩展名: 文字): 真假 {
        返回 路径.长度 > 扩展名.长度 && 尾部包括(路径, 扩展名);
    }

    导出 函数 文件扩展是其一(路径: 文字, 扩展名组: 只读数组_<文字>): 真假 {
        循环 (常量 扩展名 属于 扩展名组) {
            如果 (文件扩展是(路径, 扩展名)) {
                返回 为真;
            }
        }

        返回 为假;
    }


    导出 接口 文件系统条目_ {
        只读 文件组: 只读数组_<文字>;
        只读 目录组: 只读数组_<文字>;
    }

    导出 函数 确保脚本种类(文件名: 文字, 脚本种类: 脚本种类_ | 未定): 脚本种类_ {
        返回 脚本种类 || 获取脚本种类从文件名(文件名) || 脚本种类_.K;
    }

    导出 函数 获取脚本种类从文件名(文件名: 文字): 脚本种类_ {
        常量 ext = 文件名.子文本(文件名.最后索引位于('.'));
        假如 (ext.转为小写()) {
            若是 扩展名_.TestK:
                返回 脚本种类_.TestK;
            若是 扩展名_.DK:
                返回 脚本种类_.DK;
            若是 扩展名_.K:
                返回 脚本种类_.K;
            默认:
                返回 脚本种类_.未知_;
        }
    }

    /**
     *  List of supported extensions in order of 文件 resolution 优先级.
     */
    导出 常量 支持的扩展: 只读数组_<扩展名_> = [扩展名_.K, 扩展名_.DK, 扩展名_.TestK];
    常量 全部支持的扩展: 只读数组_<扩展名_> = 支持的扩展;

    导出 函数 获取支持的扩展(): 只读数组_<文字> {
        返回 全部支持的扩展;
    }

    导出 函数 有KLang文件扩展名(文件名: 文字) {
        返回 循环执行(支持的扩展, 扩展名 => 文件扩展是(文件名, 扩展名));
    }

    导出 函数 是支持的源文件名称(文件名: 文字) {
        如果 (!文件名) { 返回 为假; }

        循环 (常量 扩展名 属于 获取支持的扩展()) {
            如果 (文件扩展是(文件名, 扩展名)) {
                返回 为真;
            }
        }
        返回 为假;
    }

    /**
     * 扩展_ boundaries by priority. Lower numbers indicate higher priorities, and are
     * aligned to the offset of the highest priority extension in the
     * allSupportedExtensions 数组.
     */
    导出 常量 枚举 扩展优先级_ {
        Klang文件_ = 0,
        声明_ = 2,

        高 = Klang文件_,
        低 = 声明_,
    }

    导出 函数 获取扩展优先级(路径: 文字, 支持的扩展: 只读数组_<文字>): 扩展优先级_ {
        循环 (变量 i = 支持的扩展.长度 - 1; i >= 0; i--) {
            如果 (文件扩展是(路径, 支持的扩展[i])) {
                返回 调整扩展优先级(<扩展优先级_>i, 支持的扩展);
            }
        }
        返回 扩展优先级_.高;
    }

    /**
     * Adjusts an extension priority to be the highest priority within the same range.
     */
    导出 函数 调整扩展优先级(扩展优先级: 扩展优先级_, 支持的扩展: 只读数组_<文字>): 扩展优先级_ {
        如果 (扩展优先级 < 扩展优先级_.声明_) {
            返回 扩展优先级_.Klang文件_;
        }
        否则 如果 (扩展优先级 < 支持的扩展.长度) {
            返回 扩展优先级_.声明_;
        }
        否则 {
            返回 支持的扩展.长度;
        }
    }

    /**
     * Gets the next lowest extension priority for a given priority.
     */
    导出 函数 获取下个最低扩展优先级(扩展优先级: 扩展优先级_, 支持的扩展组: 只读数组_<文字>): 扩展优先级_ {
        如果 (扩展优先级 < 扩展优先级_.声明_) {
            返回 扩展优先级_.声明_;
        }
        否则 {
            返回 支持的扩展组.长度;
        }
    }

    常量 扩展名到移除 = [扩展名_.DK, 扩展名_.K, 扩展名_.TestK];
    导出 函数 删除文件扩展名(路径: 文字): 文字 {
        循环 (常量 ext 属于 扩展名到移除) {
            常量 无扩展名的 = 尝试删除扩展名(路径, ext);
            如果 (无扩展名的 !== 未定) {
                返回 无扩展名的;
            }
        }
        返回 路径;
    }

    导出 函数 尝试删除扩展名(路径: 文字, 扩展名: 文字): 文字 | 未定 {
        返回 文件扩展是(路径, 扩展名) ? 移除扩展名(路径, 扩展名) : 未定;
    }

    导出 函数 移除扩展名(路径: 文字, 扩展名: 文字): 文字 {
        返回 路径.子文字(0, 路径.长度 - 扩展名.长度);
    }

    导出 函数 更改扩展名<T 扩展 文字 | 路径_>(路径: T, 新扩展名: 文字): T {
        返回 <T>(删除文件扩展名(路径) + 新扩展名);
    }

    /**
     * Takes a string like 'jquery-min.4.2.3' and returns 'jquery'
     */
    导出 函数 删除最小值和版本号(文件名: 文字) {
        // Match a '.' or '-' followed by a version number or 'min' at the 尾位 of the 名称
        常量 尾随最小值或版本 = /[.-]((min)|(\d+(\.\d+)*))$/;

        // The 'min' or version may both be present, in either order, so try applying the above twice.
        返回 文件名.替换(尾随最小值或版本, '').替换(尾随最小值或版本, '');
    }

    导出 接口 对象构造器_ {
        获取节点构造器(): 新建 (种类: 语法_, 首位?: 数字, 尾位?: 数字) => 节点_;
        获取令牌构造器(): 新建 <TKind 扩展 语法_>(种类: TKind, 首位?: 数字, 尾位?: 数字) => 令牌_<TKind>;
        获取标识符构造器(): 新建 (种类: 语法_.标识符_, 首位?: 数字, 尾位?: 数字) => 标识符_;
        获取源文件构造器(): 新建 (种类: 语法_.源文件_, 首位?: 数字, 尾位?: 数字) => 源文件_;
        获取符号构造器(): 新建 (标志: 符号标志_, 名称: 文字) => 符号_;
        获取类型构造器(): 新建 (checker: 类型检查_, 标志: 类型标志_) => 类型_;
        获取签名构造器(): 新建 (checker: 类型检查_) => 签名_;
        获取源映射源构造器(): 新建 (文件名: 文字, 文本: 文字, 跳过杂项?: (首位: 数字) => 数字) => 源码映射源码_;
    }

    函数 符号_(本体: 符号_, 标志: 符号标志_, 名称: 文字) {
        本体.标志 = 标志;
        本体.名称 = 名称;
        本体.声明组 = 未定;
        本体.值声明 = 未定!;
        本体.id = 未定;
        本体.合并Id = 未定;
        本体.父符号 = 未定;
    }

    函数 类型_(本体: 类型_, 检查者: 类型检查_, 标志: 类型标志_) {
        本体.标志 = 标志;
        如果 (调试_.是调试中_) {
            本体.检查者 = 检查者;
        }
    }

    函数 签名_() { } // tslint:disable-line no-empty

    函数 节点_(本体: 节点_, 种类: 语法_, 首位: 数字, 尾位: 数字) {
        本体.首位 = 首位;
        本体.尾位 = 尾位;
        本体.种类 = 种类;
        本体.id = 0;
        本体.标志 = 节点标志_.无_;
        本体.修饰符标志缓存 = 修饰符组_.无_;
        本体.父节点 = 未定!;
        本体.原始 = 未定;
    }

    函数 源码映射源码_(本体: 源码映射源码_, 文件名: 文字, 文本: 文字, 跳过杂项?: (首位: 数字) => 数字) {
        本体.文件名 = 文件名;
        本体.文本 = 文本;
        本体.跳过杂项 = 跳过杂项 || (首位 => 首位);
    }

    导出 变量 对象分配器: 对象构造器_ = {
        获取节点构造器: () => <任意>节点_,
        获取令牌构造器: () => <任意>节点_,
        获取标识符构造器: () => <任意>节点_,
        获取源文件构造器: () => <任意>节点_,
        获取符号构造器: () => <任意>符号_,
        获取类型构造器: () => <任意>类型_,
        获取签名构造器: () => <任意>签名_,
        获取源映射源构造器: () => <任意>源码映射源码_,
    };


    /** Remove an item from an 数组, moving everything to its 右侧 one 空格_ 左侧. */
    导出 函数 按序删除项目<T>(数组: T[], item: T): 真假 {
        循环 (变量 i = 0; i < 数组.长度; i++) {
            如果 (数组[i] === item) {
                按序删除项目在(数组, i);
                返回 为真;
            }
        }
        返回 为假;
    }

    /** Remove an item by index from an 数组, moving everything to its 右侧 one 空格_ 左侧. */
    导出 函数 按序删除项目在<T>(数组: T[], index: 数字): 无值 {
        // This seems to be faster than either `数组.splice(i, 1)` or `数组.copyWithin(i, i+ 1)`.
        循环 (变量 i = index; i < 数组.长度 - 1; i++) {
            数组[i] = 数组[i + 1];
        }
        数组.弹出();
    }

    导出 函数 无序删除项目<T>(数组: T[], index: 数字): 无值 {
        // Fill in the 'hole' 左侧 at `index`.
        数组[index] = 数组[数组.长度 - 1];
        数组.弹出();
    }

    /** Remove the *首个* occurrence of `item` from the 数组. */
    导出 函数 无序移除项目<T>(数组: T[], item: T): 无值 {
        无序删除第一项位置(数组, 元素 => 元素 === item);
    }

    /** Remove the *首个* 元素 satisfying `predicate`. */
    函数 无序删除第一项位置<T>(数组: T[], predicate: (元素: T) => 真假): 无值 {
        循环 (变量 i = 0; i < 数组.长度; i++) {
            如果 (predicate(数组[i])) {
                无序删除项目(数组, i);
                跳出;
            }
        }
    }

    导出 类型 获取规范文件名称_ = (文件名: 文字) => 文字;
    导出 函数 创建获取规范文件名称(使用区分大小写文件名称: 真假): 获取规范文件名称_ {
        返回 使用区分大小写文件名称 ? 身份 : 转为小写;
    }

    /**
     * patternStrings contains both 模式 strings (containing '*') and regular strings.
     * Return an exact match if possible, or a 模式 match, or 未定.
     * (These are verified by verifyCompilerOptions to have 0 or 1 '*' characters.)
     */
    导出 函数 matchPatternOrExact(patternStrings: 只读数组_<文字>, candidate: 文字): 文字 | 模式_ | 未定 {
        常量 patterns: 模式_[] = [];
        循环 (常量 patternString 属于 patternStrings) {
            常量 模式 = tryParsePattern(patternString);
            如果 (模式) {
                patterns.压入(模式);
            }
            否则 如果 (patternString === candidate) {
                // 模式 was matched as is - no need to search further
                返回 patternString;
            }
        }

        返回 findBestPatternMatch(patterns, _ => _, candidate);
    }

    导出 函数 patternText({ 前缀, 后缀 }: 模式_): 文字 {
        返回 `${前缀}*${后缀}`;
    }

    /**
     * Given that candidate matches 模式, returns the 文本 matching the '*'.
     * E.g.: matchedText(tryParsePattern('foo*baz'), 'foobarbaz') === 'bar'
     */
    导出 函数 matchedText(模式: 模式_, candidate: 文字): 文字 {
        调试_.断言(isPatternMatch(模式, candidate));
        返回 candidate.子文字(模式.前缀.长度, candidate.长度 - 模式.后缀.长度);
    }

    /** Return the object corresponding to the best 模式 to match `candidate`. */
    导出 函数 findBestPatternMatch<T>(values: 只读数组_<T>, getPattern: (value: T) => 模式_, candidate: 文字): T | 未定 {
        变量 matchedValue: T | 未定;
        // use length of 前缀 as betterness criteria
        变量 longestMatchPrefixLength = -1;

        循环 (常量 v 属于 values) {
            常量 模式 = getPattern(v);
            如果 (isPatternMatch(模式, candidate) && 模式.前缀.长度 > longestMatchPrefixLength) {
                longestMatchPrefixLength = 模式.前缀.长度;
                matchedValue = v;
            }
        }

        返回 matchedValue;
    }

    函数 isPatternMatch({ 前缀, 后缀 }: 模式_, candidate: 文字) {
        返回 candidate.长度 >= 前缀.长度 + 后缀.长度 &&
            开始包含(candidate, 前缀) &&
            尾部包括(candidate, 后缀);
    }


    导出 名域 调试_ {

        导出 函数 显示符号(符号: 符号_): 文字 {
            常量 symbolFlags = (ts 转为 任意).符号标志_;
            返回 `{ 标志: ${symbolFlags ? 显示标志(符号.标志, symbolFlags) : 符号.标志}; 声明组: ${词典(符号.声明组!, 显示语法标志)} }`;
        }

        函数 显示标志(标志: 数字, flagsEnum: { [flag: 数字]: 文字 }): 文字 {
            常量 out = [];
            循环 (变量 pow = 0; pow <= 30; pow++) {
                常量 n = 1 << pow;
                如果 (标志 & n) {
                    out.压入(flagsEnum[n]);
                }
            }
            返回 out.连结('|');
        }

        导出 函数 显示令牌标志(语法: 语法_): 文字{
            常量 语法标志 = (ts 转为 任意).语法_;
            返回 语法标志[语法];
        }

        导出 函数 显示语法标志(n: 节点_): 文字 {
            常量 语法标志 = (ts 转为 任意).语法_;
            返回 语法标志 ? 语法标志[n.种类] : n.种类.转为文字();
        }
    }



    导出 函数 tryParsePattern(模式: 文字): 模式_ | 未定 {
        // This should be verified outside of here and a proper error thrown.
        调试_.断言(具有零或一个星号字符(模式));
        常量 indexOfStar = 模式.索引位于('*');
        返回 indexOfStar === -1 ? 未定 : {
            前缀: 模式.子文本(0, indexOfStar),
            后缀: 模式.子文本(indexOfStar + 1)
        };
    }

    导出 函数 位置是合成的(首位: 数字): 真假 {
        // This is a fast way of testing the following conditions:
        //  首位 === 未定 || 首位 === null || isNaN(首位) || 首位 < 0;
        返回 !(首位 >= 0);
    }

    /** 真_ if an extension is one of the supported TypeScript extensions. */
    导出 函数 扩展名是K(ext: 扩展名_): 真假 {
        返回 ext === 扩展名_.K || ext === 扩展名_.TestK || ext === 扩展名_.DK;
    }

    导出 函数 扩展名是K和DK不含TestK(路径: 文字): 真假 {
        返回 (文件扩展是(路径, 扩展名_.K) || 文件扩展是(路径, 扩展名_.DK)) && !文件扩展是(路径, 扩展名_.TestK)
    }

    /**
     * Gets the extension from a 路径.
     * 路径_ must have a valid extension.
     */
    导出 函数 扩展名从路径(路径: 文字): 扩展名_ | 未定 {
        常量 ext = 尝试获取扩展名从路径(路径);
        如果 (ext !== 未定) {
            返回 ext;
        }
        调试_.失败(`File ${路径} has 未知 extension.`);
    }

    导出 函数 是任何支撑的文件扩展名(路径: 文字): 真假 {
        返回 尝试获取扩展名从路径(路径) !== 未定;
    }

    导出 函数 尝试获取扩展名从路径(路径: 文字): 扩展名_ | 未定 {
        返回 查找<扩展名_>(全部支持的扩展, e => 文件扩展是(路径, e)) ;
    }

    // Retrieves any string from the final '.' onwards from a base 文件 名称.
    // Unlike extensionFromPath, which throws an exception on unrecognized extensions.
    导出 函数 getAnyExtensionFromPath(路径: 文字): 文字 | 未定 {
        常量 baseFileName = 获取基文件名称(路径);
        常量 extensionIndex = baseFileName!.最后索引位于('.');
        如果 (extensionIndex >= 0) {
            返回 baseFileName!.子文字(extensionIndex);
        }
    }

}

/* @internal */
名域 ts {
    导出 常量 resolvingEmptyArray: 不及[] = [] 转为 不及[];
    导出 常量 emptyMap: 词典_<不及> = 创建词典<不及>();
    导出 常量 emptyUnderscoreEscapedMap: 词典_<不及> = emptyMap 转为 词典_<不及>;

    导出 接口 ReferencePathMatchResult {
        fileReference?: 文件引用_;
        诊断信息?: 诊断信息_;
        isNoDefaultLib?: 真假;
        isTypeReferenceDirective?: 真假;
    }

    导出 函数 getDeclarationOfKind<T 扩展 声明_>(符号: 符号_, 种类: T['种类']): T | 未定 {
        常量 声明组 = 符号.声明组;
        如果 (声明组) {
            循环 (常量 declaration 属于 声明组) {
                如果 (declaration.种类 === 种类) {
                    返回 declaration 转为 T;
                }
            }
        }

        返回 未定;
    }

    常量 stringWriter = createSingleLineStringWriter();

    函数 createSingleLineStringWriter(): 输出文本作者_ {
        变量 str = '';

        常量 writeText: (文本: 文字) => 无值 = 文本 => str += 文本;
        返回 {
            获取文本: () => str,
            write: writeText,
            rawWrite: writeText,
            writeTextOfNode: writeText,
            writeKeyword: writeText,
            writeOperator: writeText,
            writePunctuation: writeText,
            writeSpace: writeText,
            writeStringLiteral: writeText,
            writeLiteral: writeText,
            writeParameter: writeText,
            writeProperty: writeText,
            writeSymbol: writeText,
            获取文本首位: () => str.长度,
            获取行: () => 0,
            获取列: () => 0,
            getIndent: () => 0,
            isAtStartOfLine: () => 为假,

            // Completely ignore indentation for string writers.  And map newlines to
            // a single 空格_.
            writeLine: () => str += ' ',
            increaseIndent: 什么都不做,
            decreaseIndent: 什么都不做,
            clear: () => str = '',
            trackSymbol: 什么都不做,
            reportInaccessibleThisError: 什么都不做,
            reportInaccessibleUniqueSymbolError: 什么都不做,
            reportPrivateInBaseOfClassExpression: 什么都不做,
        };
    }

    导出 函数 usingSingleLineStringWriter(action: (writer: 输出文本作者_) => 无值): 文字 {
        常量 oldString = stringWriter.获取文本();
        尝试 {
            action(stringWriter);
            返回 stringWriter.获取文本();
        }
        善后 {
            stringWriter.clear();
            stringWriter.writeKeyword(oldString);
        }
    }

    导出 函数 获取全部宽度(n: 节点_) {
        返回 n.尾位 - n.首位;
    }

    导出 函数 获取解析模块(源文件: 源文件_, 模块名称文本: 文字): 解析的模块名称_ | 未定 {
        返回 源文件 && 源文件.解析的模块 && 源文件.解析的模块.获取(模块名称文本);
    }

    导出 函数 设置解析模块(源文件: 源文件_, 解析名称文本: 文字, 解析模块: 解析的模块名称_): 无值 {
        如果 (!源文件.解析的模块) {
            源文件.解析的模块 = 创建词典<解析的模块名称_>();
        }

        源文件.解析的模块.设置(解析名称文本, 解析模块);
    }

    /**
     
     模块名称: 文字;
        模块目录路径: 文字;
        是启动模块: 真假;
        是外部支持库导入: 真假;
    
     */

    导出 函数 模块是相等的(旧的解析: 模块_, 新的解析: 模块_): 真假 {
        返回 旧的解析.模块名称 === 新的解析.模块名称 &&
            旧的解析.是启动模块 === 新的解析.是启动模块 &&
            旧的解析.是外部支持库导入 === 新的解析.是外部支持库导入 &&
            数组是相等的(旧的解析.源文件组, 新的解析.源文件组)
    }

    导出 函数 有更改在决议组<T>(
        名称组: 只读数组_<文字>,
        新解析: 只读数组_<T>,
        旧解析: 词典_<T>,
        比较: (旧的: T, 新的: T) => 真假): 真假 {
        调试_.断言(名称组.长度 === 新解析.长度);

        循环 (变量 i = 0; i < 名称组.长度; i++) {
            常量 新解析的 = 新解析[i];
            常量 旧解析的 = 旧解析 && 旧解析.获取(名称组[i]);
            常量 改变了 = 旧解析的 ? !新解析的 || !比较(旧解析的, 新解析的) : 新解析的;
            如果 (改变了) {
                返回 为真;
            }
        }
        返回 为假;
    }

    // Returns true if this n contains a parse error anywhere underneath it.
    导出 函数 包含分析错误(n: 节点_): 真假 {
        聚集子数据(n);
        返回 (n.标志 & 节点标志_.这个节点有错误_) !== 0;
    }

    函数 聚集子数据(n: 节点_): 无值 {
        如果 (!(n.标志 & 节点标志_.具有聚合子数据_)) {
            // A n is considered to contain a parse error if:
            //  a) the parser explicitly marked that it had an error
            //  b) any of it's children reported that it had an error.
            常量 这个节点有错误 = ((n.标志 & 节点标志_.这个节点有错误_) !== 0) ||
                遍历子节点(n, 包含分析错误);

            // If so, mark ourselves accordingly.
            如果 (这个节点有错误) {
                n.标志 |= 节点标志_.这个节点有错误_;
            }

            // Also mark that we've propagated the child information to this n.  This way we can
            // always consult the bit directly on this n without needing to check its children
            // again.
            n.标志 |= 节点标志_.具有聚合子数据_;
        }
    }

    导出 函数 获取节点的源文件(n: 节点_): 源文件_ {
        判断 (n && n.种类 !== 语法_.源文件_) {
            n = n.父节点;
        }
        返回 <源文件_>n;
    }

    导出 函数 是语句包含本地组(n: 节点_) {
        假如 (n.种类) {
            若是 语法_.块_:
            若是 语法_.若是块_:
            若是 语法_.循环语句_:
                返回 为真;
        }
        返回 为假;
    }

    导出 函数 获取行的开始点位(line: 数字, 源文件: 源文件族_): 数字 {
        调试_.断言(line >= 0);
        返回 获取行开始(源文件)[line];
    }

    // This is a useful 函数 for debugging purposes.
    导出 函数 nodePosToString(n: 节点_): 文字 {
        常量 文件 = 获取节点的源文件(n);
        常量 loc = 获取位置的行和字符(文件, n.首位);
        返回 `${文件.文件名}(${loc.行 + 1},${loc.字符 + 1})`;
    }

    导出 函数 获取结束行位置(line: 数字, 源文件: 源文件族_): 数字 {
        调试_.断言(line >= 0);
        常量 lineStarts = 获取行开始(源文件);

        常量 lineIndex = line;
        常量 源文本 = 源文件.文本;
        如果 (lineIndex + 1 === lineStarts.长度) {
            // 最后的 line - return EOF
            返回 源文本.长度 - 1;
        }
        否则 {
            // 当前 line 开始
            常量 开始 = lineStarts[lineIndex];
            // take the 开始 position of the next line - 1 = it should be some line break
            变量 首位 = lineStarts[lineIndex + 1] - 1;
            调试_.断言(是换行符(源文本.字符代码在(首位)));
            // walk backwards skipping line breaks, stop the the beginning of 当前 line.
            // i.e:
            // <some 文本>
            // $ <- 尾位 of line for this position should match the 开始 position
            判断 (开始 <= 首位 && 是换行符(源文本.字符代码在(首位))) {
                首位--;
            }
            返回 首位;
        }
    }

    // Returns true if this n is 失踪的 from the actual source code. A '失踪的' n is different
    // from '未定/defined'. When a n is 未定 (which can happen for optional nodes
    // in the tree), it is definitely 失踪的. However, a n may be defined, but still be
    // 失踪的.  This happens whenever the parser knows it needs to parse something, but can't
    // get anything in the source code that it expects at that location. For example:
    //
    //          变量 a: ;
    //
    // Here, the 类型_ in the 类型_-Annotation is not-optional (as there is a 冒号_ in the source
    // code). So the parser will attempt to parse out a 类型, and will create an actual n.
    // However, this n will be '失踪的' in the sense that no actual source-code/tokens are
    // contained within it.
    导出 函数 节点是失踪(n: 节点_ | 未定) {
        如果 (n === 未定) {
            返回 为真;
        }

        返回 n.首位 === n.尾位 && n.首位 >= 0 && n.种类 !== 语法_.文件结尾令牌_;
    }

    导出 函数 节点是存在的(n: 节点_ | 未定) {
        返回 !节点是失踪(n);
    }

    导出 函数 isPinnedComment(文本: 文字, 注释: 注释范围_) {
        返回 文本.字符代码在(注释.首位 + 1) === 字符_.星号_ &&
            文本.字符代码在(注释.首位 + 2) === 字符_.叹号_;
    }

    导出 函数 getTokenPosOfNode(n: 节点_, 源文件?: 源文件族_, includeJsDoc?: 真假): 数字 {
        // With nodes that have no width (i.e. 'Missing' nodes), we actually *don't*
        // want to skip trivia because this will launch us forward to the next 令牌.
        如果 (节点是失踪(n)) {
            返回 n.首位;
        }

        如果 (是JSDoc节点(n)) {
            返回 跳过杂项((源文件 || 获取节点的源文件(n)).文本, n.首位, /*stopAfterLineBreak*/ 为假, /*stopAtComments*/ 为真);
        }

        如果 (includeJsDoc && 具有JSDoc节点组(n)) {
            返回 getTokenPosOfNode(n.jsDoc![0]);
        }

        // For a syntax 列表, it is possible that one of its children has JsDoc注释_ nodes, while
        // the syntax 列表 itself considers them as normal trivia. Therefore if we simply skip
        // trivia for the 列表, we may have skipped the JsDoc注释_ as well. So we should process its
        // 首个 child to determine the actual position of its 首个 令牌.
        如果 (n.种类 === 语法_.语法列表_ && (<语法列表_>n)._子节点.长度 > 0) {
            返回 getTokenPosOfNode((<语法列表_>n)._子节点[0], 源文件, includeJsDoc);
        }

        返回 跳过杂项((源文件 || 获取节点的源文件(n)).文本, n.首位);
    }

    导出 函数 获取节点非装饰令牌首位(n: 节点_, 源文件?: 源文件族_): 数字 {
        如果 (节点是失踪(n)) {
            返回 getTokenPosOfNode(n, 源文件);
        }

        返回 跳过杂项((源文件 || 获取节点的源文件(n)).文本, n.首位);
    }

    导出 函数 获取源文件节点的源文本(源文件: 源文件_, n: 节点_, 包含杂项 = 为假): 文字 {
        返回 获取源文件节点的文本(源文件.文本, n, 包含杂项);
    }

    导出 函数 获取源文件节点的文本(源文本: 文字, n: 节点_, 包含杂项 = 为假): 文字 {
        如果 (节点是失踪(n)) {
            返回 '';
        }

        返回 源文本.子文字(包含杂项 ? n.首位 : 跳过杂项(源文本, n.首位), n.尾位);
    }

    导出 函数 获取节点的文本(n: 节点_, 包含杂项 = 为假): 文字 {
        返回 获取源文件节点的源文本(获取节点的源文件(n), n, 包含杂项);
    }

    函数 getPos(range: 节点_) {
        返回 range.首位;
    }

    /**
     * Note: it is expected that the `nodeArray` and the `n` are within the same 文件.
     * For example, searching for a `源文件_` in a `源文件_[]` wouldn't work.
     */
    导出 函数 indexOfNode(nodeArray: 只读数组_<节点_>, n: 节点_) {
        返回 二分搜索(nodeArray, n, getPos, 比较值);
    }

    /**
     * Gets 标志 that control emit behavior of a n.
     */
    导出 函数 getEmitFlags(n: 节点_): 输出标志_ | 未定 {
        常量 输出节点 = n.输出节点;
        返回 输出节点 && 输出节点.标志;
    }

    导出 函数 getLiteralText(n: 字面量族节点_, 源文件: 源文件_) {
        // If we don't need to downlevel and we can reach the 原始 source 文本 using
        // the n's 父节点 reference, then simply get the 文本 as it was originally written.
        如果 (!节点是合成的(n) && n.父节点 && !(是数字字面量(n) && n.数字字面量标志! & 令牌标志_.包含分隔符_)) {
            返回 获取源文件节点的源文本(源文件, n);
        }

        常量 escapeText = getEmitFlags(n)! & 输出标志_.NoAsciiEscaping ? 转义字符串 : 转义非Ascii文字;

        // If we can't reach the 原始 source 文本, use the canonical form if it's a number,
        // or a (possibly escaped) quoted form of the 原始 文本 if it's string-like.
        假如 (n.种类) {
            若是 语法_.文本字面量_:
                返回 '"' + escapeText(n.文本, 字符_.双引号_) + '"';
            若是 语法_.无替换模板字面量_:
                返回 '`' + escapeText(n.文本, 字符_.反引号_) + '`';
            若是 语法_.模板头_:
                // tslint:disable-next-line no-invalid-模板-strings
                返回 '`' + escapeText(n.文本, 字符_.反引号_) + '${';
            若是 语法_.模板中_:
                // tslint:disable-next-line no-invalid-模板-strings
                返回 '}' + escapeText(n.文本, 字符_.反引号_) + '${';
            若是 语法_.模板尾_:
                返回 '}' + escapeText(n.文本, 字符_.反引号_) + '`';
            若是 语法_.数字字面量_:
            若是 语法_.正则表达式字面量_:
                返回 n.文本;
        }

        调试_.失败(`Literal 种类 '${n.种类}' not accounted for.`);
    }

    导出 函数 getTextOfConstantValue(value: 文字 | 数字) {
        返回 是文字(value) ? '"' + 转义非Ascii文字(value) + '"' : '' + value;
    }

    /**
     * @deprecated Use `id.文本` to get the escaped 文本 of an 标识符_.
     * @param 标识符 The 标识符 to escape
     */
    导出 函数 escapeIdentifier(标识符: 文字): 文字 {
        返回 标识符;
    }

    // Make an 标识符 from an external module 名称 by extracting the string after the 最后的 '/' and replacing
    // all non-alphanumeric characters with underscores
    导出 函数 makeIdentifierFromModuleName(模块名称: 文字): 文字 {
        返回 获取基文件名称(模块名称)!.替换(/^(\d)/, '_$1').替换(/\W/g, '_');
    }

    导出 函数 是块范围容器顶层(n: 节点_): 真假 {
        返回 n.种类 === 语法_.源文件_ ||
            是函数族(n);
    }

    导出 函数 是块范围(n: 节点_, 父节点: 节点_) {
        假如 (n.种类) {
            若是 语法_.源文件_:
            若是 语法_.若是块_:
            若是 语法_.循环语句_:
            若是 语法_.循环属于语句_:
            若是 语法_.方法声明_:
            若是 语法_.函数声明_:
            若是 语法_.函数表达式_:
            若是 语法_.箭头函数_:
                返回 为真;
            若是 语法_.块_:
                返回 父节点 && !是函数族(父节点);
        }

        返回 为假;
    }

    导出 函数 是声明包括类型参数(n: 节点_): n 作为 声明包括类型参数_;
    导出 函数 是声明包括类型参数(n: 声明包括类型参数_): n 作为 声明包括类型参数_ {
        假如 (n.种类) {
            若是 语法_.方法签名_:
            若是 语法_.函数类型_:
            若是 语法_.函数签名_:
            若是 语法_.创建函数签名_:
            若是 语法_.类型别名声明_:
            若是 语法_.函数声明_:
            若是 语法_.方法声明_:
            若是 语法_.函数表达式_:
            若是 语法_.箭头函数_:
            若是 语法_.类型声明_:
            若是 语法_.结构声明_:
            若是 语法_.接口声明_:
                返回 为真;
            默认:
                断言类型不及(n);
                返回 为假;
        }
    }

    // Gets the nearest enclosing 块 scope container that has the provided n
    // as a descendant, that is not the provided n.
    导出 函数 获取封闭块范围容器(n: 节点_): 节点_ | 未定 {
        变量 当前 = n.父节点;
        判断 (当前) {
            如果 (是块范围(当前, 当前.父节点)) {
                返回 当前;
            }

            当前 = 当前.父节点;
        }
    }

    导出 函数 声明名称转为文本(名称: 名称_) {
        返回 获取全部宽度(名称) === 0 ? '(失踪)' : 获取节点的文本(名称);
    }

    导出 函数 isTypeParameterDeclaration(node: 节点_): node 作为 类型参数声明_ {
        返回 node.种类 === 语法_.类型参数声明_;
    }

    导出 函数 获取属性名称的文本(名称: 名称_): 文字 | 未定 {
        假如 (名称.种类) {
            若是 语法_.标识符_:
                返回 名称.文本;
            若是 语法_.限定名_:
                返回 实体名称转文本(名称)
            若是 语法_.真假_:
                返回 '真假'
            若是 语法_.通用_:
                返回 '通用'
            若是 语法_.整数_:
                返回 '整数'
            若是 语法_.整数8_:
                返回 '整数8'
            若是 语法_.整数16_:
                返回 '整数16'
            若是 语法_.整数32_:
                返回 '整数32'
            若是 语法_.整数64_:
                返回 '整数16'
            若是 语法_.正整数_:
                返回 '整数64'
            若是 语法_.正整数8_:
                返回 '正整数8'
            若是 语法_.正整数16_:
                返回 '正整数16'
            若是 语法_.正整数32_:
                返回 '正整数32'
            若是 语法_.正整数64_:
                返回 '正整数64'
            若是 语法_.小数_:
                返回 '小数'
            若是 语法_.小数32_:
                返回 '小数32'
            若是 语法_.小数64_:
                返回 '小数64'
            若是 语法_.复数64_:
                返回 '复数64'
            若是 语法_.复数128_:
                返回 '复数128'
            若是 语法_.字节_:
                返回 '字节'
            若是 语法_.字符_:
                返回 '字符'
            若是 语法_.文本_:
                返回 '文本'
            若是 语法_.无值_:
                返回 '无值'
            若是 语法_.未定_:
                返回 '未定'
            默认:
                调试_.断言不及(名称);
        }
    }

    导出 函数 实体名称转文本(名称: 实体名或实体名表达式_): 文字 {
        假如 (名称.种类) {
            若是 语法_.标识符_:
                返回 获取全部宽度(名称) === 0 ? idText(名称) : 获取节点的文本(名称);
            若是 语法_.限定名_:
                返回 实体名称转文本(名称.左侧) + '.' + 实体名称转文本(名称.右侧);
            若是 语法_.属性访问表达式_:
                返回 实体名称转文本(名称.表达式) + '.' + 实体名称转文本(名称.名称);
        }
    }

    导出 函数 创建诊断从节点(n: 节点_, 消息: 诊断信息_, 实参0?: 文字 | 数字, 实参1?: 文字 | 数字, 实参2?: 文字 | 数字, 实参3?: 文字 | 数字): 诊断_ {
        常量 源文件 = 获取节点的源文件(n);
        返回 创建诊断从节点在源文件(源文件, n, 消息, 实参0, 实参1, 实参2, 实参3);
    }

    导出 函数 创建诊断从节点数组(源文件: 源文件_, ns: 节点数组_<节点_>, 消息: 诊断信息_, 实参0?: 文字 | 数字, 实参1?: 文字 | 数字, 实参2?: 文字 | 数字, 实参3?: 文字 | 数字): 诊断_ {
        常量 开始 = 跳过杂项(源文件.文本, ns.首位);
        返回 创建文件诊断信息(源文件, 开始, ns.尾位 - 开始, 消息, 实参0!, 实参1!, 实参2!, 实参3!);
    }

    导出 函数 创建诊断从节点在源文件(源文件: 源文件_, n: 节点_, 消息: 诊断信息_, 实参0?: 文字 | 数字, 实参1?: 文字 | 数字, 实参2?: 文字 | 数字, 实参3?: 文字 | 数字): 诊断_ {
        常量 跨度 = 获取错误跨度从节点(源文件, n);
        返回 创建文件诊断信息(源文件, 跨度.开始, 跨度.长度, 消息, 实参0!, 实参1!, 实参2!, 实参3!);
    }

    导出 函数 创建诊断从节点跨度(源文件: 源文件_, 开始节点: 节点_, 结束节点: 节点_, 消息: 诊断信息_, 实参0?: 文字 | 数字, 实参1?: 文字 | 数字, 实参2?: 文字 | 数字, 实参3?: 文字 | 数字): 诊断_ {
        常量 开始 = 跳过杂项(源文件.文本, 开始节点.首位);
        返回 创建文件诊断信息(源文件, 开始, 结束节点.尾位 - 开始, 消息, 实参0!, 实参1!, 实参2!, 实参3!);
    }

    导出 函数 创建诊断从节点从消息链(n: 节点_, 消息链: 诊断信息链_): 诊断_ {
        常量 源文件 = 获取节点的源文件(n);
        常量 跨度 = 获取错误跨度从节点(源文件, n);
        返回 {
            文件: 源文件,
            开始: 跨度.开始,
            _长度: 跨度.长度,
            代码: 消息链.代码,
            级别: 消息链.级别,
            消息文本: 消息链.下个 ? 消息链 : 消息链.消息文本
        };
    }

    导出 函数 获取令牌在位置的跨度(源文件: 源文件_, 首位: 数字): 文本跨度_ {
        常量 扫描器 = 创建扫描器( /*跳过杂项*/ 为真, 源文件.文本, /*onError:*/ 未定, 首位);
        扫描器.扫描();
        常量 开始 = 扫描器.获取令牌首位();
        返回 从边界创建文本范围(开始, 扫描器.获取文本首位());
    }

    函数 获取错误跨度从箭头函数(源文件: 源文件_, n: 箭头函数_): 文本跨度_ {
        常量 首位 = 跳过杂项(源文件.文本, n.首位);
        如果 (n.主体 && n.主体.种类 === 语法_.块_) {
            常量 { 行: 开始行 } = 获取位置的行和字符(源文件, n.主体.首位);
            常量 { 行: 结束行 } = 获取位置的行和字符(源文件, n.主体.尾位);
            如果 (开始行 < 结束行) {
                // The arrow 函数 spans multiple lines,
                // make the error 跨度 be the 首个 line, inclusive.
                返回 创建文本跨度(首位, 获取结束行位置(开始行, 源文件) - 首位 + 1);
            }
        }
        返回 从边界创建文本范围(首位, n.尾位);
    }

    导出 函数 获取错误跨度从节点(源文件: 源文件_, n: 节点_): 文本跨度_ {
        变量 错误节点 = n;
        假如 (n.种类) {
            若是 语法_.源文件_:
                常量 首位 = 跳过杂项(源文件.文本, 0, /*stopAfterLineBreak*/ 为假);
                如果 (首位 === 源文件.文本.长度) {
                    返回 创建文本跨度(0, 0);
                }
                返回 获取令牌在位置的跨度(源文件, 首位);
            若是 语法_.变量声明_:
            若是 语法_.接口声明_:
            若是 语法_.函数声明_:
            若是 语法_.函数表达式_:
            若是 语法_.方法声明_:
            若是 语法_.类型别名声明_:
                错误节点 = (<变量声明_>n).名称!;
                跳出;
            若是 语法_.箭头函数_:
                返回 获取错误跨度从箭头函数(源文件, <箭头函数_>n);
        }

        如果 (错误节点 === 未定) {
            返回 获取令牌在位置的跨度(源文件, n.首位);
        }

        常量 是失踪的 = 节点是失踪(错误节点);
        常量 首位 = 是失踪的
            ? 错误节点.首位
            : 跳过杂项(源文件.文本, 错误节点.首位);
        如果 (是失踪的) {
            调试_.断言(首位 === 错误节点.首位, 'This failure could trigger https://github.com/Microsoft/TypeScript/issues/20809');
            调试_.断言(首位 === 错误节点.尾位, 'This failure could trigger https://github.com/Microsoft/TypeScript/issues/20809');
        }
        否则 {
            调试_.断言(首位 >= 错误节点.首位, 'This failure could trigger https://github.com/Microsoft/TypeScript/issues/20809');
            调试_.断言(首位 <= 错误节点.尾位, 'This failure could trigger https://github.com/Microsoft/TypeScript/issues/20809');
        }

        返回 从边界创建文本范围(首位, 错误节点.尾位);
    }

    导出 函数 是常量(n: 节点_): 真假 {
        返回 !!(获取组合节点标志(n) & 节点标志_.常量_) || !!(获取组合修饰符标志(n) & 修饰符组_.常量_);
    }

    导出 函数 是变量(n: 节点_): 真假 {
        返回 !!(获取组合节点标志(n) & 节点标志_.变量_);
    }

    导出 函数 是序言指令(n: 节点_): n 作为 序言指令_ {
        返回 n.种类 === 语法_.表达式语句_ && (<表达式语句_>n).表达式.种类 === 语法_.文本字面量_;
    }

    导出 函数 获取节点的前导注释范围(n: 节点_, 节点的源文件: 源文件_) {
        返回 获取前导注释范围(节点的源文件.文本, n.首位);
    }

    导出 函数 获取JSDoc注释范围(n: 节点_, 文本: 文字) {
        常量 注释范围 = (n.种类 === 语法_.参数_ ||
            n.种类 === 语法_.函数表达式_ ||
            n.种类 === 语法_.箭头函数_ ||
            n.种类 === 语法_.括号表达式_) ?
            组合(获取跟踪注释范围(文本, n.首位)!, 获取前导注释范围(文本, n.首位)!) :
            获取前导注释范围(文本, n.首位);
        返回 过滤器(注释范围!, 注释 =>
            文本.字符代码在(注释.首位! + 1) === 字符_.星号_ &&
            文本.字符代码在(注释.首位! + 2) === 字符_.星号_ &&
            文本.字符代码在(注释.首位! + 3) !== 字符_.斜杠号_);
    }

    导出 函数 是类型节点的一部分(n: 节点_): 真假 {
        如果 (语法_.首类型节点_ <= n.种类 && n.种类 <= 语法_.尾类型节点_) {
            返回 为真;
        }
        假如 (n.种类) {
            若是 语法_.通用_:
            若是 语法_.整数_:
            若是 语法_.文本_:
            若是 语法_.真假_:
            若是 语法_.未定_:
                返回 为真;
            若是 语法_.无值_:
                返回 n.父节点.种类 !== 语法_.无值表达式_;
            若是 语法_.标识符_:
                // If the 标识符 is the RHS of a qualified 名称, then it's a 类型 iff its 父节点 is.
                如果 (n.父节点.种类 === 语法_.限定名_ && (<限定名_>n.父节点).右侧 === n) {
                    n = n.父节点;
                }
                否则 如果 (n.父节点.种类 === 语法_.属性访问表达式_ && (<属性访问表达式_>n.父节点).名称 === n) {
                    n = n.父节点;
                }
                // At this point, n is either a qualified 名称 or an 标识符
                调试_.断言(n.种类 === 语法_.标识符_ || n.种类 === 语法_.限定名_ || n.种类 === 语法_.属性访问表达式_, '"n" was expected to be a qualified 名称, 标识符 or property access in "isPartOfTypeNode".');
            // falls through
            若是 语法_.限定名_:
            若是 语法_.属性访问表达式_:
                常量 父节点 = n.父节点;
                如果 (语法_.首类型节点_ <= 父节点.种类 && 父节点.种类 <= 语法_.尾类型节点_) {
                    返回 为真;
                }
                假如 (父节点.种类) {
                    若是 语法_.属性声明_:
                    若是 语法_.参数_:
                    若是 语法_.变量声明_:
                        返回 n === (父节点 转为 有类型_).类型;
                    若是 语法_.函数声明_:
                    若是 语法_.函数表达式_:
                    若是 语法_.箭头函数_:
                    若是 语法_.方法声明_:
                    若是 语法_.方法签名_:
                        如果 ((<有类型_>父节点).类型) {

                        }
                        返回 为假;
                    若是 语法_.类型断言表达式_:
                    若是 语法_.调用表达式_:
                    若是 语法_.新建表达式_:
                    若是 语法_.标记模板表达式_:
                        返回 为假;
                }
        }
        返回 为假;
    }

    导出 函数 isRightSideOfQualifiedNameOrPropertyAccess(node: 节点_) {
        返回 (node.父节点.种类 === 语法_.限定名_ && (<限定名_>node.父节点).右侧 === node) ||
            (node.父节点.种类 === 语法_.属性访问表达式_ && (<属性访问表达式_>node.父节点).名称 === node);
    }

    导出 函数 isChildOfNodeWithKind(n: 节点_, 种类: 语法_): 真假 {
        判断 (n) {
            如果 (n.种类 === 种类) {
                返回 为真;
            }
            n = n.父节点;
        }
        返回 为假;
    }

    // Warning: This has the same semantics as the forEach family of functions,
    //          in that traversal terminates in the event that 'visitor' supplies a truthy value.
    导出 函数 forEachReturnStatement<T>(主体: 块_, visitor: (stmt: 返回语句_) => T): T {

        返回 traverse(主体)!;

        函数 traverse(n: 节点_): T | 未定 {
            假如 (n.种类) {
                若是 语法_.返回语句_:
                    返回 visitor(<返回语句_>n);
                若是 语法_.若是块_:
                若是 语法_.块_:
                若是 语法_.如果语句_:
                若是 语法_.循环语句_:
                若是 语法_.循环属于语句_:
                若是 语法_.假如语句_:
                若是 语法_.若是子句_:
                若是 语法_.默认子句_:
                若是 语法_.标签语句_:
                    返回 遍历子节点(n, traverse);
            }
        }
    }

    /**
     * Gets the most likely 元素 类型 for a 类型节点_. This is not an exhaustive test
     * as it assumes a rest argument can only be an 数组 类型 (either T[], or Array<T>).
     *
     * @param n The 类型 n.
     */
    导出 函数 获取剩余参数元素类型(n: 类型节点_) {
        如果 (n && n.种类 === 语法_.数组类型_) {
            返回 (<数组类型节点_>n).元素类型;
        }
        返回 未定;
    }

    导出 函数 获取声明的成员(n: 声明_): 节点数组_<属性声明_ | 方法签名_ | 类型节点_ |  结构声明属性组_> | 未定 {
        假如 (n.种类) {
            若是 语法_.接口声明_:
                返回 (<接口声明_>n).成员;
            若是 语法_.结构声明_:
                返回 (<结构声明_>n).成员;
        }
    }

    导出 函数 是变量族(n: 节点_): n 作为 变量族声明_ {
        如果 (n) {
            假如 (n.种类) {
                若是 语法_.常量声明_:
                若是 语法_.参数_:
                若是 语法_.属性声明_:
                若是 语法_.变量声明_:
                    返回 为真;
            }
        }
        返回 为假;
    }

    导出 函数 是变量声明在变量语句_(n: 变量声明_) {
        返回 n.父节点.种类 === 语法_.变量语句_;
    }

    导出 函数 是有效的ES符号声明(n: 节点_) {
        返回 是变量声明(n) ? 是常量(n) && 是标识符(n.名称) && 是变量声明在变量语句_(n) :
            是属性声明(n) ? 具有只读修饰符(n) && 具有静态修饰符(n) :
            具有只读修饰符(n);
    }

    导出 函数 是引入外部对象参数(n: 节点_) {
        假如 (n.种类) {
            若是 语法_.方法声明_:
            若是 语法_.方法签名_:
            若是 语法_.函数声明_:
            若是 语法_.函数表达式_:
                返回 为真;
        }
        返回 为假;
    }

    导出 函数 打开标签最里面的语句(n: 标签语句_, 在打开之前标签回调?: (n: 标签语句_) => 无值) {
        判断 (为真) {
            如果 (在打开之前标签回调) {
                在打开之前标签回调(n);
            }
            如果 (n.语句.种类 !== 语法_.标签语句_) {
                返回 n.语句;
            }
            n = <标签语句_>n.语句;
        }
    }

    导出 函数 是函数块(n: 节点_) {
        返回 n && n.种类 === 语法_.块_ && 是函数族(n.父节点);
    }

    导出 函数 获取包含函数(n: 节点_): 签名声明_ {
        返回 查找祖先(n.父节点!, 是函数族)!;
    }

    导出 函数 获取立即调用的函数表达式(func: 节点_): 调用表达式_ | 未定 {
        如果 (func.种类 === 语法_.函数表达式_ || func.种类 === 语法_.箭头函数_) {
            变量 prev = func;
            变量 父节点 = func.父节点;
            判断 (父节点.种类 === 语法_.括号表达式_) {
                prev = 父节点;
                父节点 = 父节点.父节点;
            }
            如果 (父节点.种类 === 语法_.调用表达式_ && (父节点 转为 调用表达式_).表达式 === prev) {
                返回 父节点 转为 调用表达式_;
            }
        }
    }

    导出 函数 获取实体名称从类型节点(n: 类型节点_): 实体名或实体名表达式_ | 未定 {
        如果 (是指针类型节点(n)) {
            获取实体名称从类型节点((<指针类型节点_>n).基础类型)
        }
        假如 (n.种类) {
            若是 语法_.类型引用_:
                返回 (<类型引用节点_>n).类型名;
            若是 语法_.标识符_:
            若是 语法_.限定名_:
                返回 (<实体名称_><节点_>n);
        }

        返回 未定;
    }

    导出 函数 是表达式节点(n: 节点_): 真假 {
        假如 (n.种类) {
            若是 语法_.真_:
            若是 语法_.假_:
            若是 语法_.正则表达式字面量_:
            若是 语法_.数组字面量表达式_:
            若是 语法_.结构字面量表达式_:
            若是 语法_.属性访问表达式_:
            若是 语法_.元素访问表达式_:
            若是 语法_.调用表达式_:
            若是 语法_.新建表达式_:
            若是 语法_.标记模板表达式_:
            若是 语法_.类型转换表达式_:
            若是 语法_.类型断言表达式_:
            若是 语法_.括号表达式_:
            若是 语法_.函数表达式_:
            若是 语法_.箭头函数_:
            若是 语法_.无值表达式_:
            若是 语法_.前缀一元表达式_:
            若是 语法_.后缀一元表达式_:
            若是 语法_.二元表达式_:
            若是 语法_.条件表达式_:
            若是 语法_.展开元素_:
            若是 语法_.模板表达式_:
            若是 语法_.无替换模板字面量_:
            若是 语法_.省略表达式_:
                返回 为真;
            若是 语法_.限定名_:
                判断 (n.父节点.种类 === 语法_.限定名_) {
                    n = n.父节点;
                }
                返回 为假;
            若是 语法_.标识符_:
                返回 为假;
            // falls through
            若是 语法_.数字字面量_:
            若是 语法_.文本字面量_:
                返回 是位于表达式上下文(n);
            默认:
                返回 为假;
        }
    }

    导出 函数 是位于表达式上下文(n: 节点_): 真假 {
        常量 父节点 = n.父节点;
        假如 (父节点.种类) {
            若是 语法_.变量声明_:
            若是 语法_.参数_:
            若是 语法_.属性声明_:
                返回 为假;
            若是 语法_.表达式语句_:
            若是 语法_.如果语句_:
            若是 语法_.返回语句_:
            若是 语法_.假如语句_:
            若是 语法_.若是子句_:
                返回 (<表达式语句_>父节点).表达式 === n;
            若是 语法_.循环语句_:
                返回 为假;
            若是 语法_.循环属于语句_:
                返回 为假;
            若是 语法_.类型断言表达式_:
            若是 语法_.类型转换表达式_:
                返回 n === (<断言表达式_>父节点).表达式;
            若是 语法_.模板跨度_:
                返回 n === (<模板跨度_>父节点).表达式;
            默认:
                返回 是表达式节点(父节点);
        }
    }


    导出 函数 是位于JSDoc(n: 节点_): 真假 {
        返回 n && !!(n.标志 & 节点标志_.JSDoc);
    }

    导出 函数 是单或双引号(charCode: 数字) {
        返回 charCode === 字符_.单引号_ || charCode === 字符_.双引号_;
    }

    导出 函数 是文本双引号(str: 文本字面量_, 源文件: 源文件_): 真假 {
        返回 获取源文件节点的源文本(源文件, str).字符代码在(0) === 字符_.双引号_;
    }

    导出 函数 获取扩展的声明(n: 节点_): 节点_ | 未定 {
        如果 (!n.父节点) {
            返回 未定;
        }
        变量 名称: 表达式_ | 未定;
        变量 decl: 节点_ | 未定;
        如果 (是二元表达式(n.父节点) && n.父节点.运算符令牌.种类 === 语法_.等号_ && n.父节点.右侧 === n) {
            名称 = n.父节点.左侧;
            decl = 名称;
        }
        否则 如果 (是二元表达式(n.父节点) && n.父节点.运算符令牌.种类 === 语法_.双或号_) {
            
            如果 (是二元表达式(n.父节点.父节点) && n.父节点.父节点.运算符令牌.种类 === 语法_.等号_ && n.父节点.父节点.右侧 === n.父节点) {
                名称 = n.父节点.父节点.左侧;
                decl = 名称;
            }

            如果 (!名称 || !是实体名称表达式(名称) || !是同一实体名称(名称, n.父节点.左侧)) {
                返回 未定;
            }
        }
        返回 decl;
    }

    导出 函数 是赋值声明(decl: 声明_) {
        返回 是二元表达式(decl) || 是属性访问表达式(decl) || 是标识符(decl) || 是调用表达式(decl);
    }


    导出 函数 是文本或数组字面量族(n: 节点_): n 作为 字面量族_ | 数字字面量_ {
        返回 是文本字面量族(n) || 是数字字面量(n);
    }


    /** Given an expando 初始化, return its declaration 名称, or the 左侧-hand side of the assignment if it's part of an assignment declaration. */
    导出 函数 是表达式的名称(n: 声明_): 名称_ | 未定 {
        如果 (是二元表达式(n.父节点)) {
            常量 父节点 = (n.父节点.运算符令牌.种类 === 语法_.双或号_ && 是二元表达式(n.父节点.父节点)) ? n.父节点.父节点 : n.父节点;
            如果 (父节点.运算符令牌.种类 === 语法_.等号_ && 是标识符(父节点.左侧)) {
                返回 父节点.左侧;
            }
        }
        否则 如果 (是变量声明(n.父节点)) {
            返回 n.父节点.名称;
        }
    }



    /**
     * Is the 'declared' 名称 the same as the one in the 初始化?
     * @return true for identical 实体 names, as well as ones where the 初始化 is prefixed with
     * 'window', 'self' or 'global'. For example:
     *
     * var my = my || {}
     * var min = window.min || {}
     * my.app = self.my.app || class { }
     */
    函数 是同一实体名称(名称: 实体名表达式_, 初始化: 表达式_): 真假 {
        如果 (是标识符(名称) && 是标识符(初始化)) {
            返回 名称.文本 === 初始化.文本;
        }
        如果 (是标识符(名称) && 是属性访问表达式(初始化)) {
            返回 (初始化.表达式.种类 || 是标识符(初始化.表达式)) && 是同一实体名称(名称, 初始化.名称);
        }
        如果 (是属性访问表达式(名称) && 是属性访问表达式(初始化)) {
            返回 名称.名称.文本 === 初始化.名称.文本 && 是同一实体名称(名称.表达式, 初始化.表达式);
        }
        返回 为假;
    }

    导出 函数 获取最右侧赋值表达式(n: 表达式_): 表达式_ {
        判断 (是赋值表达式(n, /*excludeCompoundAssignements*/ 为真)) {
            n = n.右侧;
        }
        返回 n;
    }

    导出 函数 是别名符号声明(n: 节点_): 真假 {
        返回 n.种类 === 语法_.导入声明_ ||
            n.种类 === 语法_.导入成员_ ||
            n.种类 === 语法_.导入从声明_ ||
            n.种类 === 语法_.导入从成员_;
    }

    导出 函数 获取JSDoc注释和标签(n: 节点_): 只读数组_<JSDoc_ | JSDoc标签_> {
        变量 结果: (JSDoc_ | JSDoc标签_)[] | 未定;
        获取JSDoc注释和标签平台(n);
        返回 结果 || 空数组;

        函数 获取JSDoc注释和标签平台(n: 节点_): 无值 {
            常量 父节点 = n.父节点;
            如果 (父节点 && (父节点.种类 === 语法_.属性声明_)) {
                获取JSDoc注释和标签平台(父节点);
            }
            如果 (具有JSDoc节点组(n)) {
                结果 = 添加范围(结果, n.jsDoc);
            }
        }
    }



    导出 函数 获取JSDoc主机(n: JSDoc标签_): 有JSDoc_ {
        调试_.断言(n.父节点!.种类 === 语法_.JsDoc注释_);
        返回 n.父节点!.父节点!;
    }

    导出 函数 具有剩余参数(s: 签名声明_): 真假 {
        常量 最后的 = 最后或未定义(s.参数!);
        返回 !!最后的 && 是剩余参数(最后的);
    }

    导出 函数 是剩余参数(n: 参数声明_): 真假 {
        返回 !!(n.类型 && n.类型.种类 === 语法_.变长参数类型_);
    }

    导出 常量 枚举 赋值种类_ {
        无_, 确定的_, 复合_
    }

    导出 函数 获取赋值目标种类(n: 节点_): 赋值种类_ {
        变量 父节点 = n.父节点;
        判断 (为真) {
            假如 (父节点.种类) {
                若是 语法_.二元表达式_:
                    常量 二元运算符 = (<二元表达式_>父节点).运算符令牌.种类;
                    返回 是赋值运算符(二元运算符) && (<二元表达式_>父节点).左侧 === n ?
                        二元运算符 === 语法_.等号_ ? 赋值种类_.确定的_ : 赋值种类_.复合_ :
                        赋值种类_.无_;
                若是 语法_.前缀一元表达式_:
                若是 语法_.后缀一元表达式_:
                    常量 一元运算符 = (<前缀一元表达式_ | 后缀一元表达式_>父节点).运算符;
                    返回 一元运算符 === 语法_.双加号_ || 一元运算符 === 语法_.双减号_ ? 赋值种类_.复合_ : 赋值种类_.无_;
                若是 语法_.循环属于语句_:
                    返回 (<循环属于语句_>父节点).表达式 === n ? 赋值种类_.确定的_ : 赋值种类_.无_;
                若是 语法_.括号表达式_:
                若是 语法_.数组字面量表达式_:
                若是 语法_.展开元素_:
                    n = 父节点;
                    跳出;
                默认:
                    返回 赋值种类_.无_;
            }
            父节点 = n.父节点;
        }
    }

    // A n is an assignment 目标 if it is on the 左侧 hand side of an '=' 令牌, if it is parented by a property
    // assignment in an object 字面量 that is an assignment 目标, or if it is parented by an 数组 字面量 that is
    // an assignment 目标. Examples include 'a = xxx', '{ p: a } = xxx', '[{ a }] = xxx'.
    // (Note that `p` is not a 目标 in the above examples, only `a`.)
    导出 函数 是赋值标签(n: 节点_): 真假 {
        返回 获取赋值目标种类(n) !== 赋值种类_.无_;
    }

    导出 类型 具有可能已悬挂声明的节点_ =
        | 块_
        | 变量语句_
        | 如果语句_
        | 假如语句_
        | 若是块_
        | 若是子句_
        | 默认子句_
        | 标签语句_
        | 循环语句_
        | 循环属于语句_
        | 循环属于语句_;

    /**
     * Indicates whether a n could contain a `var` 变量声明列表_ that contributes to
     * the same `var` declaration scope as the n's 父节点.
     */
    导出 函数 是具有可能已悬挂声明的节点(n: 节点_): n 作为 具有可能已悬挂声明的节点_ {
        假如 (n.种类) {
            若是 语法_.块_:
            若是 语法_.变量语句_:
            若是 语法_.如果语句_:
            若是 语法_.假如语句_:
            若是 语法_.若是块_:
            若是 语法_.若是子句_:
            若是 语法_.默认子句_:
            若是 语法_.标签语句_:
            若是 语法_.循环语句_:
            若是 语法_.循环属于语句_:
            若是 语法_.循环属于语句_:
                返回 为真;
        }
        返回 为假;
    }

    函数 向上(n: 节点_, 种类: 语法_) {
        判断 (n && n.种类 === 种类) {
            n = n.父节点;
        }
        返回 n;
    }

    导出 函数 向上翻过括号类型(n: 节点_) {
        返回 向上(n, 语法_.括号类型_);
    }

    导出 函数 向上翻过括号表达式(n: 节点_) {
        返回 向上(n, 语法_.括号表达式_);
    }

    导出 函数 跳过括号(n: 表达式_): 表达式_;
    导出 函数 跳过括号(n: 节点_): 节点_;
    导出 函数 跳过括号(n: 节点_): 节点_ {
        判断 (n.种类 === 语法_.括号表达式_) {
            n = (<括号表达式_>n).表达式;
        }

        返回 n;
    }

    导出 函数 是节点后代(n: 节点_, 祖先: 节点_): 真假 {
        判断 (n) {
            如果 (n === 祖先) 返回 为真;
            n = n.父节点;
        }
        返回 为假;
    }

    // 真_ if `名称` is the 名称 of a declaration n
    导出 函数 是声明名称(名称: 节点_): 真假 {
        返回 !是源文件(名称) && 是声明(名称.父节点) && 名称.父节点.名称 === 名称;
    }

    // See GH#16030
    导出 函数 是任意声明名称(名称: 节点_): 真假 {
        假如 (名称.种类) {
            若是 语法_.标识符_:
            若是 语法_.文本字面量_:
            若是 语法_.数字字面量_:
                如果 (是声明(名称.父节点)) {
                    返回 名称.父节点.名称 === 名称;
                }
                常量 二元表达式 = 名称.父节点.父节点;
                返回 是二元表达式(二元表达式) && 获取声明名称(二元表达式) === 名称;
            默认:
                返回 为假;
        }
    }

    导出 函数 是字面量计算属性声明名称(n: 节点_) {
        返回 (n.种类 === 语法_.文本字面量_ || n.种类 === 语法_.数字字面量_) && 
            是声明(n.父节点.父节点);
    }

    // Return true if the given 标识符 is classified as an IdentifierName
    导出 函数 是标识符名称(n: 标识符_): 真假 {
        变量 父节点 = n.父节点;
        假如 (父节点.种类) {
            若是 语法_.属性声明_:
            若是 语法_.方法声明_:
            若是 语法_.方法签名_:
            若是 语法_.属性访问表达式_:
                // Name in member declaration or property 名称 in property access
                返回 (<名称声明_ | 属性访问表达式_>父节点).名称 === n;
        }
        返回 为假;
    }

    导出 函数 尝试解析脚本引用(host: 脚本引用主机_, 源文件: 源文件_, 引用: 文件引用_) {
        如果 (!host.获取编译选项().noResolve) {
            常量 引用文件名称 = 是根磁盘路径(引用.文件名) ? 引用.文件名 : 合并路径(获取目录路径(源文件.文件名), 引用.文件名);
            返回 host.获取源文件(引用文件名称);
        }
    }

    导出 函数 获取祖先(n: 节点_ | 未定, 种类: 语法_): 节点_ | 未定 {
        判断 (n) {
            如果 (n.种类 === 种类) {
                返回 n;
            }
            n = n.父节点;
        }
        返回 未定;
    }

    导出 函数 是关键字(令牌: 语法_): 真假 {
        返回 语法_.首关键字_ <= 令牌 && 令牌 <= 语法_.尾关键字_;
    }

    导出 类型 杂项种类_ = 语法_.单行注释杂项_
        | 语法_.多行注释杂项_
        | 语法_.新行杂项_
        | 语法_.空白杂项_
        | 语法_.指令杂项_
        | 语法_.冲突标记杂项_;

    导出 函数 是杂项(令牌: 语法_): 令牌 作为 杂项种类_ {
        返回 语法_.首杂项令牌_ <= 令牌 && 令牌 <= 语法_.尾杂项令牌_;
    }

    导出 常量 枚举 函数标志_ {
        普通_ = 0,             // 函数_ is a normal 函数
        无效的 = 1 << 2
    }

    导出 函数 获取函数标志(n: 签名声明_ | 未定) {
        如果 (!n) {
            返回 函数标志_.无效的;
        }
        返回 函数标志_.普通_;
    }


    导出 函数 是文本或数字字面量(n: 节点_): n 作为 文本字面量_ | 数字字面量_ {
        常量 种类 = n.种类;
        返回 种类 === 语法_.文本字面量_
            || 种类 === 语法_.数字字面量_;
    }


    导出 函数 获取属性名从属性名节点(名称: 名称_): 文字 {
        如果 (名称.种类 === 语法_.标识符_) {
            返回 名称.文本;
        }
        返回 未定!;
    }

    导出 类型 属性名称字面量_ = 标识符_ | 字面量族_ | 数字字面量_;
    导出 函数 是属性名称字面量(n: 节点_): n 作为 属性名称字面量_ {
        假如 (n.种类) {
            若是 语法_.标识符_:
            若是 语法_.文本字面量_:
            若是 语法_.无替换模板字面量_:
            若是 语法_.数字字面量_:
                返回 为真;
            默认:
                返回 为假;
        }
    }
    导出 函数 获取标识符或字面量文本(n: 属性名称字面量_): 文字 {
        返回 n.种类 === 语法_.标识符_ ? idText(n) : n.文本;
    }

    导出 函数 getEscapedTextOfIdentifierOrLiteral(n: 属性名称字面量_): 文字 {
        返回  n.文本
    }

    导出 函数 是压入或推入标识符(n: 标识符_) {
        返回 n.文本 === '压入' || n.文本 === '推入';
    }

    导出 函数 是参数声明(n: 变量族声明_) {
        返回 n.种类 === 语法_.参数_;
    }

    导出 函数 获取根声明(n: 节点_): 节点_ {
        返回 n;
    }

    导出 函数 节点开始新词汇环境(n: 节点_): 真假 {
        常量 种类 = n.种类;
        返回 种类 === 语法_.函数表达式_
            || 种类 === 语法_.函数声明_
            || 种类 === 语法_.箭头函数_
            || 种类 === 语法_.方法声明_ 
            || 种类 === 语法_.源文件_;
    }

    导出 函数 节点是合成的(range: 文本范围_): 真假 {
        返回 位置是合成的(range.首位)
            || 位置是合成的(range.尾位);
    }

    导出 函数 获取元素源文件(源文件: 源文件_) {
        返回 获取解析树节点(源文件, 是源文件) || 源文件;
    }

    导出 常量 枚举 结合性_ {
        左_,
        右_
    }

    导出 函数 获取表达式结合性(表达式: 表达式_) {
        常量 运算符 = 获取运算符(表达式);
        返回 获取运算符结合性(表达式.种类, 运算符);
    }

    导出 函数 获取运算符结合性(种类: 语法_, 运算符: 语法_) {
        假如 (种类) {
            若是 语法_.新建表达式_:
                返回 结合性_.左_ ;

            若是 语法_.前缀一元表达式_:
            若是 语法_.无值表达式_:
            若是 语法_.条件表达式_:
                返回 结合性_.右_;

            若是 语法_.二元表达式_:
                假如 (运算符) {
                    若是 语法_.等号_:
                    若是 语法_.加等号_:
                    若是 语法_.减等号_:
                    若是 语法_.星等号_:
                    若是 语法_.斜杠等号_:
                    若是 语法_.百分等号_:
                    若是 语法_.双左尖等号_:
                    若是 语法_.双右尖等号_:
                    若是 语法_.且等号_:
                    若是 语法_.异或等号_:
                    若是 语法_.或等号_:
                        返回 结合性_.右_;
                }
        }
        返回 结合性_.左_;
    }

    导出 函数 获取表达式优先级(表达式: 表达式_) {
        常量 运算符 = 获取运算符(表达式);
        返回 获取运算符优先级(表达式.种类, 运算符);
    }

    导出 函数 获取运算符(表达式: 表达式_) {
        如果 (表达式.种类 === 语法_.二元表达式_) {
            返回 (<二元表达式_>表达式).运算符令牌.种类;
        }
        否则 如果 (表达式.种类 === 语法_.前缀一元表达式_ || 表达式.种类 === 语法_.后缀一元表达式_) {
            返回 (<前缀一元表达式_ | 后缀一元表达式_>表达式).运算符;
        }
        否则 {
            返回 表达式.种类;
        }
    }

    导出 函数 获取运算符优先级(节点种类: 语法_, 运算符种类: 语法_) {
        假如 (节点种类) {
            若是 语法_.标识符_:
            若是 语法_.真_:
            若是 语法_.假_:
            若是 语法_.数字字面量_:
            若是 语法_.文本字面量_:
            若是 语法_.数组字面量表达式_:
            若是 语法_.结构字面量表达式_:
            若是 语法_.函数表达式_:
            若是 语法_.箭头函数_:
            若是 语法_.正则表达式字面量_:
            若是 语法_.无替换模板字面量_:
            若是 语法_.模板表达式_:
            若是 语法_.括号表达式_:
            若是 语法_.省略表达式_:
                返回 19;

            若是 语法_.标记模板表达式_:
            若是 语法_.属性访问表达式_:
            若是 语法_.元素访问表达式_:
                返回 18;

            若是 语法_.新建表达式_:
                返回  17;

            若是 语法_.调用表达式_:
                返回 17;

            若是 语法_.后缀一元表达式_:
                返回 16;

            若是 语法_.前缀一元表达式_:
            若是 语法_.无值表达式_:
                返回 15;

            若是 语法_.二元表达式_:
                假如 (运算符种类) {
                    若是 语法_.叹号_:
                    若是 语法_.波折号_:
                        返回 15;
                    若是 语法_.星号_:
                    若是 语法_.斜杠号_:
                    若是 语法_.百分号_:
                        返回 14;

                    若是 语法_.加号_:
                    若是 语法_.减号_:
                        返回 13;

                    若是 语法_.双左尖号_:
                    若是 语法_.双右尖号_:
                        返回 12;

                    若是 语法_.左尖号_:
                    若是 语法_.左尖等号_:
                    若是 语法_.右尖号_:
                    若是 语法_.右尖等号_:
                    若是 语法_.位于_:
                        返回 11;

                    若是 语法_.双等号_:
                    若是 语法_.叹号等号_:
                        返回 10;

                    若是 语法_.且号_:
                        返回 9;

                    若是 语法_.异或号_:
                        返回 8;

                    若是 语法_.或号_:
                        返回 7;

                    若是 语法_.双且号_:
                        返回 6;

                    若是 语法_.双或号_:
                        返回 5;
                    若是 语法_.等号_:
                    若是 语法_.加等号_:
                    若是 语法_.减等号_:
                    若是 语法_.星等号_:
                    若是 语法_.斜杠等号_:
                    若是 语法_.百分等号_:
                    若是 语法_.双左尖等号_:
                    若是 语法_.双右尖等号_:
                    若是 语法_.且等号_:
                    若是 语法_.异或等号_:
                    若是 语法_.或等号_:
                        返回 3;

                    若是 语法_.逗号_:
                        返回 0;

                    默认:
                        返回 -1;
                }

            若是 语法_.条件表达式_:
                返回 4;
            若是 语法_.展开元素_:
                返回 1;
            若是 语法_.逗号列表表达式_:
                返回 0;

            默认:
                返回 -1;
        }
    }

    导出 函数 创建诊断集合(): 诊断收集_ {
        变量 无文件诊断信息组 = [] 转为 诊断_[] 转为 排序数组_<诊断_>; // See GH#19873
        常量 文件包括诊断信息 = [] 转为 文字[] 转为 排序数组_<文字>;
        常量 文件诊断信息 = 创建词典<排序数组_<诊断_>>();
        变量 具有读取非文件诊断 = 为假;
        变量 修改计数 = 0;

        返回 {
            添加: 添加,
            获取全局诊断,
            获取诊断,
            获取修改计数,
            重新附加文件诊断
        };

        函数 获取修改计数() {
            返回 修改计数;
        }

        函数 重新附加文件诊断(新文件: 源文件_): 无值 {
            循环执行(文件诊断信息.获取(新文件.文件名), 诊断的 => 诊断的.文件 = 新文件);
        }

        函数 添加(诊断的: 诊断_): 无值 {
            变量 诊断组: 排序数组_<诊断_> | 未定;
            如果 (诊断的.文件) {
                诊断组 = 文件诊断信息.获取(诊断的.文件.文件名)!;
                如果 (!诊断组) {
                    诊断组 = [] 转为 诊断_[] 转为 排序数组_<诊断_>;
                    文件诊断信息.设置(诊断的.文件.文件名, 诊断组);
                    插入排序(文件包括诊断信息, 诊断的.文件.文件名, 比较字符串区分大小写);
                }
            }
            否则 {
                // If we've already read the non-文件 diagnostics, do not modify the existing 数组.
                如果 (具有读取非文件诊断) {
                    具有读取非文件诊断 = 为假;
                    无文件诊断信息组 = 无文件诊断信息组.分切() 转为 排序数组_<诊断_>;
                }

                诊断组 = 无文件诊断信息组;
            }

            插入排序(诊断组, 诊断的, 比较诊断);
            修改计数++;
        }

        函数 获取全局诊断(): 诊断_[] {
            具有读取非文件诊断 = 为真;
            返回 无文件诊断信息组;
        }

        函数 获取诊断(文件名?: 文字): 诊断_[] {
            如果 (文件名) {
                返回 文件诊断信息.获取(文件名) || [];
            }

            常量 文件诊断 = 扁平词典(文件包括诊断信息, f => 文件诊断信息.获取(f));
            如果 (!无文件诊断信息组.长度) {
                返回 文件诊断!;
            }
            文件诊断!.推入(...无文件诊断信息组);
            返回 文件诊断!;
        }
    }

    // This consists of the 首个 19 unprintable ASCII characters, canonical escapes, 行分隔符_,
    // 段落分隔符_, and 下一行_. The latter three are just desirable to suppress new lines in
    // the language service. These characters should be escaped when printing, and if any characters are added,
    // the map below must be updated. Note that this regexp *does not* include the 'delete' character.
    // There is no reason for this other than that JSON.stringify does not handle it either.
    常量 doubleQuoteEscapedCharsRegExp = /[\\\'\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
    常量 singleQuoteEscapedCharsRegExp = /[\\\'\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
    常量 backtickQuoteEscapedCharsRegExp = /[\\\`\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
    常量 转义字符词典 = 创建词典从模板({
        '\t': '\\t',
        '\v': '\\v',
        '\f': '\\f',
        '\b': '\\b',
        '\r': '\\r',
        '\n': '\\n',
        '\\': '\\\\',
        "\"": "\\\"",
        "\'": "\\\'",
        '\`': '\\\`',
        '\u2028': '\\u2028', // 行分隔符_
        '\u2029': '\\u2029', // 段落分隔符_
        '\u0085': '\\u0085'  // 下一行_
    });

    /**
     * Based heavily on the abstract 'Quote'/'QuoteJSONString' operation from ECMA-262 (24.3.2.2),
     * but augmented for a few select characters (e.g. 行分隔符_, 段落分隔符_, 下一行_)
     * Note that this doesn't actually wrap the input in double quotes.
     */
    导出 函数 转义字符串(s: 文字, 引号字符?: 字符_.双引号_ | 字符_.单引号_ | 字符_.反引号_): 文字 {
        常量 转义字符正则 =
            引号字符 === 字符_.反引号_ ? backtickQuoteEscapedCharsRegExp :
                引号字符 === 字符_.单引号_ ? singleQuoteEscapedCharsRegExp :
                    doubleQuoteEscapedCharsRegExp;
        返回 s.替换(转义字符正则, 获取更换);
    }

    函数 获取更换(c: 文字, offset: 数字, input: 文字) {
        如果 (c.字符代码在(0) === 字符_.空字符_) {
            常量 前瞻 = input.字符代码在(offset + c.长度);
            如果 (前瞻 >= 字符_._0 && 前瞻 <= 字符_._9) {
                // If the null character is followed by digits, print as a hex escape to prevent the 结果 from parsing as an octal (which is forbidden in strict mode)
                返回 '\\x00';
            }
            // Otherwise, keep printing a 字面量 \0 for the null character
            返回 '\\0';
        }
        返回 转义字符词典.获取(c) || 获取16位Unic转义规范(c.字符代码在(0));
    }

    导出 函数 isIntrinsicJsxName(名称: 文字 | 文字) {
        常量 ch = (名称 转为 文字).字符代码在(0);
        返回 (ch >= 字符_.a && ch <= 字符_.z) || (名称 转为 文字).索引位于('-') > -1;
    }

    函数 获取16位Unic转义规范(charCode: 数字): 文字 {
        常量 hexCharCode = charCode.转为文字(16).转为大写();
        常量 paddedHexCode = ('0000' + hexCharCode).分切(-4);
        返回 '\\u' + paddedHexCode;
    }

    常量 非Ascii字符正则 = /[^\u0000-\u007F]/g;
    导出 函数 转义非Ascii文字(s: 文字, quoteChar?: 字符_.双引号_ | 字符_.单引号_ | 字符_.反引号_): 文字 {
        s = 转义字符串(s, quoteChar);
        // Replace non-ASCII characters with '\uNNNN' escapes if any exist.
        // Otherwise just return the 原始 string.
        返回 非Ascii字符正则.测试(s) ?
            s.替换(非Ascii字符正则, c => 获取16位Unic转义规范(c.字符代码在(0))) :
            s;
    }

    常量 indentStrings: 文字[] = ['', '    '];
    导出 函数 getIndentString(level: 数字) {
        如果 (indentStrings[level] === 未定) {
            indentStrings[level] = getIndentString(level - 1) + indentStrings[1];
        }
        返回 indentStrings[level];
    }

    导出 函数 getIndentSize() {
        返回 indentStrings[1].长度;
    }

    导出 函数 创建文本作者(newLine: 文字): 输出文本作者_ {
        变量 output: 文字;
        变量 缩进: 数字;
        变量 lineStart: 真假;
        变量 lineCount: 数字;
        变量 linePos: 数字;

        函数 write(s: 文字) {
            如果 (s && s.长度) {
                如果 (lineStart) {
                    output += getIndentString(缩进);
                    lineStart = 为假;
                }
                output += s;
            }
        }

        函数 reset(): 无值 {
            output = '';
            缩进 = 0;
            lineStart = 为真;
            lineCount = 0;
            linePos = 0;
        }

        函数 rawWrite(s: 文字) {
            如果 (s !== 未定) {
                如果 (lineStart) {
                    lineStart = 为假;
                }
                output += s;
            }
        }

        函数 writeLiteral(s: 文字) {
            如果 (s && s.长度) {
                write(s);
                常量 lineStartsOfS = 计算行开始(s);
                如果 (lineStartsOfS.长度 > 1) {
                    lineCount = lineCount + lineStartsOfS.长度 - 1;
                    linePos = output.长度 - s.长度 + 最后或未定义(lineStartsOfS)!;
                }
            }
        }

        函数 writeLine() {
            如果 (!lineStart) {
                output += newLine;
                lineCount++;
                linePos = output.长度;
                lineStart = 为真;
            }
        }

        函数 writeTextOfNode(文本: 文字, n: 节点_) {
            write(获取源文件节点的文本(文本, n));
        }

        reset();

        返回 {
            write,
            rawWrite,
            writeTextOfNode,
            writeLiteral,
            writeLine,
            increaseIndent: () => { 缩进++; },
            decreaseIndent: () => { 缩进--; },
            getIndent: () => 缩进,
            获取文本首位: () => output.长度,
            获取行: () => lineCount + 1,
            获取列: () => lineStart ? 缩进 * getIndentSize() + 1 : output.长度 - linePos + 1,
            获取文本: () => output,
            isAtStartOfLine: () => lineStart,
            clear: reset,
            reportInaccessibleThisError: 什么都不做,
            reportPrivateInBaseOfClassExpression: 什么都不做,
            reportInaccessibleUniqueSymbolError: 什么都不做,
            trackSymbol: 什么都不做,
            writeKeyword: write,
            writeOperator: write,
            writeParameter: write,
            writeProperty: write,
            writePunctuation: write,
            writeSpace: write,
            writeStringLiteral: write,
            writeSymbol: write
        };
    }

    导出 函数 获取解析的外部模块名(host: 输出主机_, 文件: 源文件_): 文字 {
        返回 文件.模块名称 || 获取外部模块名从路径(host, 文件.文件名);
    }

    导出 函数 获取外部模块名称从声明(host: 输出主机_, resolver: 输出解析器_, declaration: 导入从声明_ | 导入声明_ ): 文字 {
        常量 文件 = resolver.获取外部模块文件从声明(declaration); 
        如果 (!文件 || 文件.是声明文件) {
            返回 未定!;
        }
        返回 获取解析的外部模块名(host, 文件);
    }

    /**
     * Resolves a local 路径 to a 路径 which is absolute to the base of the emit
     */
    导出 函数 获取外部模块名从路径(host: 输出主机_, 文件名: 文字): 文字 {
        常量 获取规范文件名称 = (f: 文字) => host.获取规范文件名称(f);
        常量 目录 = 转为路径(host.获取公共源目录(), host.获取当前目录(), 获取规范文件名称);
        常量 文件路径 = 获取标准化绝对路径(文件名, host.获取当前目录())!;
        常量 相对路径 = 获取相对路径到目录或URL(目录, 文件路径, 目录, 获取规范文件名称, /*isAbsolutePathAnUrl*/ 为假)!;
        返回 删除文件扩展名(相对路径);
    }

    导出 函数 获取主输出输出文件路径(源文件: 源文件_, host: 输出主机_, 扩展名: 文字) {
        常量 编译选项 = host.获取编译选项();
        变量 输出输出文件路径包括扩展名: 文字;
        如果 (编译选项.输出) {
            输出输出文件路径包括扩展名 = 删除文件扩展名(获取源文件路径在新目录(源文件, host, '输出'));
        }
        否则 {
            输出输出文件路径包括扩展名 = 删除文件扩展名(源文件.文件名);
        }

        返回 输出输出文件路径包括扩展名 + 扩展名;
    }

    导出 函数 获取声明输出输出文件路径(源文件: 源文件_, host: 输出主机_) {
        常量 路径 = 获取源文件路径在新目录(源文件, host,  '输出')
        返回 删除文件扩展名(路径) + 扩展名_.DK;
    }

    导出 接口 输出文件名称_ {
        输出文件路径: 文字
    }

    /**
     * Gets the source files that are expected to have an emit output.
     *
     * Originally part of `forEachExpectedEmitFile`, this functionality was extracted to support
     * transformations.
     *
     * @param host An 输出主机_.
     * @param 目标源文件 An optional 目标 source 文件 to emit.
     */
    导出 函数 获取源文件到输出(host: 输出主机_, 目标源文件?: 源文件_): 只读数组_<源文件_> {
        常量 options = host.获取编译选项();
        常量 是来自外部支持库的源文件 = (文件: 源文件_) => host.是来自外部库的源文件(文件);
        如果 (options.outFile || options.out) {
            返回 过滤器(host.获取源文件组(), 源文件 => 源文件可能已发出(源文件,  是来自外部支持库的源文件));
        }
        否则 {
            常量 源文件组 = 目标源文件 === 未定 ? host.获取源文件组() : [目标源文件];
            返回 过滤器(源文件组, 源文件 => 源文件可能已发出(源文件,  是来自外部支持库的源文件));
        }
    }

    /** Don't call this for `--outFile`, just for `--outDir` or plain emit. `--outFile` needs additional checks. */
    导出 函数 源文件可能已发出(源文件: 源文件_, 是源文件从外部支持库: (文件: 源文件_) => 真假) {
        返回 !源文件.是声明文件 && !是源文件从外部支持库(源文件);
    }

    导出 函数 获取源文件路径在新目录(源文件: 源文件_, host: 输出主机_, 新目录路径: 文字) {
        变量 sourceFilePath = 获取标准化绝对路径(源文件.文件名, host.获取当前目录())!;
        常量 commonSourceDirectory = host.获取公共源目录();
        常量 isSourceFileInCommonSourceDirectory = host.获取规范文件名称(sourceFilePath).索引位于(host.获取规范文件名称(commonSourceDirectory)) === 0;
        sourceFilePath = isSourceFileInCommonSourceDirectory ? sourceFilePath.子文字(commonSourceDirectory.长度) : sourceFilePath;
        返回 合并路径(新目录路径, sourceFilePath);
    }

    导出 函数 writeFile(host: 输出主机_, diagnostics: 诊断收集_, 文件名: 文字, data: 文字, writeByteOrderMark: 真假, sourceFiles?: 只读数组_<源文件_>) {
        host.写文件(文件名, data, writeByteOrderMark, hostErrorMessage => {
            diagnostics.添加(创建编译器诊断(诊断信息_.Could_not_write_file_0_Colon_1, 文件名, hostErrorMessage));
        }, sourceFiles!);
    }

    导出 函数 getLineOfLocalPosition(currentSourceFile: 源文件_, 首位: 数字) {
        返回 获取位置的行和字符(currentSourceFile, 首位).行;
    }

    导出 函数 getLineOfLocalPositionFromLineMap(行词典: 只读数组_<数字>, 首位: 数字) {
        返回 计算位置的行和字符(行词典, 首位).行;
    }

    导出 函数 输出新行之前前导注释(行词典: 只读数组_<数字>, writer: 输出文本作者_, n: 文本范围_, leadingComments: 只读数组_<注释范围_>) {
        输出位置的新行之的前前导注释组(行词典, writer, n.首位, leadingComments);
    }

    导出 函数 输出位置的新行之的前前导注释组(行词典: 只读数组_<数字>, writer: 输出文本作者_, 首位: 数字, leadingComments: 只读数组_<注释范围_>) {
        // If the leading 注释组 开始 on different line than the 开始 of n, write new line
        如果 (leadingComments && leadingComments.长度 && 首位 !== leadingComments[0].首位 &&
            getLineOfLocalPositionFromLineMap(行词典, 首位) !== getLineOfLocalPositionFromLineMap(行词典, leadingComments[0].首位)) {
            writer.writeLine();
        }
    }

    导出 函数 输出位置的新行之的前前导注释(行词典: 只读数组_<数字>, writer: 输出文本作者_, 首位: 数字, commentPos: 数字) {
        // If the leading 注释组 开始 on different line than the 开始 of n, write new line
        如果 (首位 !== commentPos &&
            getLineOfLocalPositionFromLineMap(行词典, 首位) !== getLineOfLocalPositionFromLineMap(行词典, commentPos)) {
            writer.writeLine();
        }
    }

    导出 函数 输出注释(
        文本: 文字,
        行词典: 只读数组_<数字>,
        writer: 输出文本作者_,
        注释组: 只读数组_<注释范围_>,
        leadingSeparator: 真假,
        trailingSeparator: 真假,
        newLine: 文字,
        写注释: (文本: 文字, 行词典: 只读数组_<数字>, writer: 输出文本作者_, commentPos: 数字, commentEnd: 数字, newLine: 文字) => 无值) {
        如果 (注释组 && 注释组.长度 > 0) {
            如果 (leadingSeparator) {
                writer.write(' ');
            }

            变量 emitInterveningSeparator = 为假;
            循环 (常量 注释 属于 注释组) {
                如果 (emitInterveningSeparator) {
                    writer.write(' ');
                    emitInterveningSeparator = 为假;
                }

                写注释(文本, 行词典, writer, 注释.首位, 注释.尾位, newLine);
                如果 (注释.有尾随新行) {
                    writer.writeLine();
                }
                否则 {
                    emitInterveningSeparator = 为真;
                }
            }

            如果 (emitInterveningSeparator && trailingSeparator) {
                writer.write(' ');
            }
        }
    }

    /**
     * Detached 注释 is a 注释 at the top of 文件 or 函数 主体 that is separated from
     * the next 语句 by 空格_.
     */
    导出 函数 输出分离注释(文本: 文字, 行词典: 只读数组_<数字>, writer: 输出文本作者_,
        writeComment: (文本: 文字, 行词典: 只读数组_<数字>, writer: 输出文本作者_, commentPos: 数字, commentEnd: 数字, newLine: 文字) => 无值,
        n: 文本范围_, newLine: 文字, removeComments: 真假) {
        变量 leadingComments: 注释范围_[];
        变量 currentDetachedCommentInfo: { nodePos: 数字, detachedCommentEndPos: 数字 };
        如果 (removeComments) {
            // removeComments is true, only reserve pinned 注释 at the top of 文件
            // For example:
            //      /*! Pinned Comment */
            //
            //      var x = 10;
            如果 (n.首位 === 0) {
                leadingComments = 过滤器(获取前导注释范围(文本, n.首位)!, isPinnedCommentLocal);
            }
        }
        否则 {
            // removeComments is false, just get detached as normal and bypass the process to filter 注释
            leadingComments = 获取前导注释范围(文本, n.首位)!;
        }

        如果 (leadingComments!) {
            常量 detachedComments: 注释范围_[] = [];
            变量 lastComment: 注释范围_;

            循环 (常量 注释 属于 leadingComments!) {
                如果 (lastComment!) {
                    常量 lastCommentLine = getLineOfLocalPositionFromLineMap(行词典, lastComment!.尾位);
                    常量 commentLine = getLineOfLocalPositionFromLineMap(行词典, 注释.首位);

                    如果 (commentLine >= lastCommentLine + 2) {
                        // There was a blank line between the 最后的 注释 and this 注释.  This
                        // 注释 is not part of the copyright 注释组.  Return what we have so
                        // far.
                        跳出;
                    }
                }

                detachedComments.压入(注释);
                lastComment = 注释;
            }

            如果 (detachedComments.长度) {
                // All 注释组 look like they could have been part of the copyright header.  Make
                // sure there is at least one blank line between it and the n.  If not, it's not
                // a copyright header.
                常量 lastCommentLine = getLineOfLocalPositionFromLineMap(行词典, 最后或未定义(detachedComments!)!.尾位);
                常量 nodeLine = getLineOfLocalPositionFromLineMap(行词典, 跳过杂项(文本, n.首位));
                如果 (nodeLine >= lastCommentLine + 2) {
                    // Valid detachedComments
                    输出新行之前前导注释(行词典, writer, n, leadingComments);
                    输出注释(文本, 行词典, writer, detachedComments, /*leadingSeparator*/ 为假, /*trailingSeparator*/ 为真, newLine, writeComment);
                    currentDetachedCommentInfo = { nodePos: n.首位, detachedCommentEndPos: 最后或未定义(detachedComments!)!.尾位 };
                }
            }
        }

        返回 currentDetachedCommentInfo!;

        函数 isPinnedCommentLocal(注释: 注释范围_) {
            返回 isPinnedComment(文本, 注释);
        }

    }

    导出 函数 写注释范围(文本: 文字, 行词典: 只读数组_<数字>, writer: 输出文本作者_, commentPos: 数字, commentEnd: 数字, newLine: 文字) {
        如果 (文本.字符代码在(commentPos + 1) === 字符_.星号_) {
            常量 firstCommentLineAndCharacter = 计算位置的行和字符(行词典, commentPos);
            常量 lineCount = 行词典.长度;
            变量 firstCommentLineIndent!: 数字;
            循环 (变量 首位 = commentPos, currentLine = firstCommentLineAndCharacter.行; 首位 < commentEnd; currentLine++) {
                常量 nextLineStart = (currentLine + 1) === lineCount
                    ? 文本.长度 + 1
                    : 行词典[currentLine + 1];

                如果 (首位 !== commentPos) {
                    // If we are not emitting 首个 line, we need to write the spaces to adjust the alignment
                    如果 (firstCommentLineIndent === 未定) {
                        firstCommentLineIndent = 计算缩进(文本, 行词典[firstCommentLineAndCharacter.行], commentPos);
                    }

                    // These are number of spaces writer is going to write at 当前 缩进
                    常量 currentWriterIndentSpacing = writer.getIndent() * getIndentSize();

                    // Number of spaces we want to be writing
                    // eg: Assume writer 缩进
                    // module m {
                    //         /* starts at character 9 this is line 1
                    //    * starts at character 首位 4 line                        --1  = 8 - 8 + 3
                    //   More 左侧 indented 注释 */                            --2  = 8 - 8 + 2
                    //     class c { }
                    // }
                    // module m {
                    //     /* this is line 1 -- Assume 当前 writer 缩进 8
                    //      * line                                                --3 = 8 - 4 + 5
                    //            More 右侧 indented 注释 */                  --4 = 8 - 4 + 11
                    //     class c { }
                    // }
                    常量 spacesToEmit = currentWriterIndentSpacing - firstCommentLineIndent + 计算缩进(文本, 首位, nextLineStart);
                    如果 (spacesToEmit > 0) {
                        变量 numberOfSingleSpacesToEmit = spacesToEmit % getIndentSize();
                        常量 indentSizeSpaceString = getIndentString((spacesToEmit - numberOfSingleSpacesToEmit) / getIndentSize());

                        // Write 缩进 size string ( in eg 1: = '', 2: '' , 3: string with 8 spaces 4: string with 12 spaces
                        writer.rawWrite(indentSizeSpaceString);

                        // Emit the single spaces (in eg: 1: 3 spaces, 2: 2 spaces, 3: 1 空格_, 4: 3 spaces)
                        判断 (numberOfSingleSpacesToEmit) {
                            writer.rawWrite(' ');
                            numberOfSingleSpacesToEmit--;
                        }
                    }
                    否则 {
                        // No spaces to emit write empty string
                        writer.rawWrite('');
                    }
                }

                // Write the 注释 line 文本
                写入修剪的当前行(文本, commentEnd, writer, newLine, 首位, nextLineStart);

                首位 = nextLineStart;
            }
        }
        否则 {
            // Single line 注释 of style //....
            writer.write(文本.子文字(commentPos, commentEnd));
        }
    }

    函数 写入修剪的当前行(文本: 文字, commentEnd: 数字, writer: 输出文本作者_, newLine: 文字, 首位: 数字, nextLineStart: 数字) {
        常量 尾位 = 数学_.min(commentEnd, nextLineStart - 1);
        常量 currentLineText = 文本.子文字(首位, 尾位).替换(/^\s+|\s+$/g, '');
        如果 (currentLineText) {
            // trimmed forward and ending spaces 文本
            writer.write(currentLineText);
            如果 (尾位 !== commentEnd) {
                writer.writeLine();
            }
        }
        否则 {
            // Empty string - make sure we write empty line
            writer.writeLiteral(newLine);
        }
    }

    函数 计算缩进(文本: 文字, 首位: 数字, 尾位: 数字) {
        变量 currentLineIndent = 0;
        循环 (; 首位 < 尾位 && 是空白单行(文本.字符代码在(首位)); 首位++) {
            如果 (文本.字符代码在(首位) === 字符_.水平制表号_) {
                // Tabs = TabSize = 缩进 size and go to next tabStop
                currentLineIndent += getIndentSize() - (currentLineIndent % getIndentSize());
            }
            否则 {
                // Single 空格_
                currentLineIndent++;
            }
        }

        返回 currentLineIndent;
    }



    导出 函数 具有修饰符组(n: 节点_) {
        返回 getModifierFlags(n) !== 修饰符组_.无_;
    }

    导出 函数 具有修饰符(n: 节点_, 标志: 修饰符组_): 真假 {
        返回 !!获取所选修饰符标志(n, 标志);
    }

    导出 函数 具有静态修饰符(n: 节点_): 真假 {
        返回 具有修饰符(n, 修饰符组_.Static);
    }

    导出 函数 具有只读修饰符(n: 节点_): 真假 {
        返回 具有修饰符(n, 修饰符组_.Readonly);
    }

    导出 函数 获取所选修饰符标志(n: 节点_, 标志: 修饰符组_): 修饰符组_ | 未定 {
        返回 getModifierFlags(n)! & 标志;
    }

    导出 函数 getModifierFlags(n: 节点_): 修饰符组_ {
        如果 (n.修饰符标志缓存! & 修饰符组_.具有计算标志_) {
            返回 n.修饰符标志缓存! & ~修饰符组_.具有计算标志_;
        }

        常量 标志 = getModifierFlagsNoCache(n);
        n.修饰符标志缓存 = 标志 | 修饰符组_.具有计算标志_;
        返回 标志;
    }

    导出 函数 getModifierFlagsNoCache(n: 节点_): 修饰符组_ {

        变量 标志 = 修饰符组_.无_;
        如果 (n.种类 === 语法_.标识符_ && (<标识符_>n).是在JSDoc名域) {
            标志 |= 修饰符组_.导出_;
        }

        返回 标志;
    }

    导出 函数 modifierToFlag(令牌: 语法_): 修饰符组_ {
        假如 (令牌) {
            若是 语法_.常量_: 返回 修饰符组_.常量_;
        }
        返回 修饰符组_.无_;
    }

    导出 函数 isLogicalOperator(令牌: 语法_): 真假 {
        返回 令牌 === 语法_.双或号_
            || 令牌 === 语法_.双且号_
            || 令牌 === 语法_.叹号_;
    }

    导出 函数 是赋值运算符(令牌: 语法_): 真假 {
        返回 令牌 >= 语法_.首赋值_ && 令牌 <= 语法_.尾赋值_;
    }

    导出 函数 是赋值表达式(n: 节点_, 排除复合赋值: 为真): n 作为 赋值表达式_<等号_>;
    导出 函数 是赋值表达式(n: 节点_, 排除复合赋值?: 为假): n 作为 赋值表达式_<赋值运算符令牌_>;
    导出 函数 是赋值表达式(n: 节点_, 排除复合赋值?: 真假): n 作为 赋值表达式_<赋值运算符令牌_> {
        返回 是二元表达式(n)
            && (排除复合赋值
                ? n.运算符令牌.种类 === 语法_.等号_
                : 是赋值运算符(n.运算符令牌.种类))
            && 是左侧表达式(n.左侧);
    }

    导出 函数 是实体名称表达式(n: 节点_): n 作为 实体名表达式_ {
        返回 n.种类 === 语法_.标识符_ ||
            n.种类 === 语法_.属性访问表达式_ && 是实体名称表达式((<属性访问表达式_>n).表达式);
    }
    导出 函数 是表达式包括类型参数(n: 节点_): n 作为 表达式包括类型参数_ {
        返回 n.种类 === 语法_.表达式包括类型参数_
    }

    导出 函数 是限定名称或属性访问的右侧(n: 节点_) {
        返回 (n.父节点.种类 === 语法_.限定名_ && (<限定名_>n.父节点).右侧 === n) ||
            (n.父节点.种类 === 语法_.属性访问表达式_ && (<属性访问表达式_>n.父节点).名称 === n);
    }

    导出 函数 是空数组字面量(表达式: 节点_): 真假 {
        返回 表达式.种类 === 语法_.数组字面量表达式_ &&
            (<数组字面量表达式_>表达式).元素.长度 === 0;
    }

    /** Return '.ts', '.d.ts', or '.tsx', if that is the extension. */
    导出 函数 尝试提取KLang扩展名(文件名: 文字): 文字 | 未定 {
        返回 查找(支持的扩展, 扩展名 => 文件扩展是(文件名, 扩展名));
    }
    /**
     * Replace each instance of non-ascii characters by one, two, three, or four escape sequences
     * representing the UTF-8 encoding of the character, and return the expanded char code 列表.
     */
    函数 获取扩展字符代码(input: 文字): 数字[] {
        常量 output: 数字[] = [];
        常量 length = input.长度;

        循环 (变量 i = 0; i < length; i++) {
            常量 charCode = input.字符代码在(i);

            // handel utf8
            如果 (charCode < 0x80) {
                output.压入(charCode);
            }
            否则 如果 (charCode < 0x800) {
                output.压入((charCode >> 6) | 0B11000000);
                output.压入((charCode & 0B00111111) | 0B10000000);
            }
            否则 如果 (charCode < 0x10000) {
                output.压入((charCode >> 12) | 0B11100000);
                output.压入(((charCode >> 6) & 0B00111111) | 0B10000000);
                output.压入((charCode & 0B00111111) | 0B10000000);
            }
            否则 如果 (charCode < 0x20000) {
                output.压入((charCode >> 18) | 0B11110000);
                output.压入(((charCode >> 12) & 0B00111111) | 0B10000000);
                output.压入(((charCode >> 6) & 0B00111111) | 0B10000000);
                output.压入((charCode & 0B00111111) | 0B10000000);
            }
            否则 {
                调试_.断言(为假, 'Unexpected code point');
            }
        }

        返回 output;
    }

    常量 base64Digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

    /**
     * Converts a string to a base-64 encoded ASCII string.
     */
    导出 函数 convertToBase64(input: 文字): 文字 {
        变量 结果 = '';
        常量 charCodes = 获取扩展字符代码(input);
        变量 i = 0;
        常量 length = charCodes.长度;
        变量 byte1: 数字, byte2: 数字, byte3: 数字, byte4: 数字;

        判断 (i < length) {
            // Convert every 6-bits in the input 3 character points
            // into a base64 digit
            byte1 = charCodes[i] >> 2;
            byte2 = (charCodes[i] & 0B00000011) << 4 | charCodes[i + 1] >> 4;
            byte3 = (charCodes[i + 1] & 0B00001111) << 2 | charCodes[i + 2] >> 6;
            byte4 = charCodes[i + 2] & 0B00111111;

            // We are out of characters in the input, set the extra
            // digits to 64 (padding character).
            如果 (i + 1 >= length) {
                byte3 = byte4 = 64;
            }
            否则 如果 (i + 2 >= length) {
                byte4 = 64;
            }

            // Write to the output
            结果 += base64Digits.字符在(byte1) + base64Digits.字符在(byte2) + base64Digits.字符在(byte3) + base64Digits.字符在(byte4);

            i += 3;
        }

        返回 结果;
    }

    常量 carriageReturnLineFeed = '\r\n';
    常量 换行符_ = '\n';
    导出 函数 获取新行字符(options: 编译选项_ | 打印机选项_, getNewLine?: () => 文字): 文字 {
        假如 (options.newLine) {
            若是 NewLineKind.CarriageReturnLineFeed:
                返回 carriageReturnLineFeed;
            若是 NewLineKind.LineFeed:
                返回 换行符_;
        }
        返回 getNewLine ? getNewLine() : sys ? sys.新行 : carriageReturnLineFeed;
    }

    /**
     * Formats an enum value as a string for debugging and debug assertions.
     */
    函数 formatEnum(value = 0, enumObject: 任意, isFlags?: 真假) {
        常量 成员 = getEnumMembers(enumObject);
        如果 (value === 0) {
            返回 成员.长度 > 0 && 成员[0][0] === 0 ? 成员[0][1] : '0';
        }
        如果 (isFlags) {
            变量 结果 = '';
            变量 remainingFlags = value;
            循环 (变量 i = 成员.长度 - 1; i >= 0 && remainingFlags !== 0; i--) {
                常量 [enumValue, enumName] = 成员[i];
                如果 (enumValue !== 0 && (remainingFlags & enumValue) === enumValue) {
                    remainingFlags &= ~enumValue;
                    结果 = `${enumName}${结果 ? ', ' : ''}${结果}`;
                }
            }
            如果 (remainingFlags === 0) {
                返回 结果;
            }
        }
        否则 {
            循环 (常量 [enumValue, enumName] 属于 成员) {
                如果 (enumValue === value) {
                    返回 enumName;
                }
            }
        }
        返回 value.转为文字();
    }

    函数 getEnumMembers(enumObject: 任意) {
        常量 结果: [数字, 文字][] = [];
        循环 (常量 名称 位于 enumObject) {
            常量 value = enumObject[名称];
            如果 (类为 value === 'number') {
                结果.压入([value, 名称]);
            }
        }

        返回 stableSort<[数字, 文字]>(结果, (x, y) => 比较值(x[0], y[0]));
    }

    导出 函数 formatSyntaxKind(种类: 语法_): 文字 {
        返回 formatEnum(种类, (<任意>ts).语法_, /*isFlags*/ 为假);
    }

    导出 函数 formatModifierFlags(标志: 修饰符组_): 文字 {
        返回 formatEnum(标志, (<任意>ts).修饰符组_, /*isFlags*/ 为真);
    }

    导出 函数 formatEmitFlags(标志: 输出标志_): 文字 {
        返回 formatEnum(标志, (<任意>ts).输出标志_, /*isFlags*/ 为真);
    }

    导出 函数 formatSymbolFlags(标志: 符号标志_): 文字 {
        返回 formatEnum(标志, (<任意>ts).符号标志_, /*isFlags*/ 为真);
    }

    导出 函数 formatTypeFlags(标志: 类型标志_): 文字 {
        返回 formatEnum(标志, (<任意>ts).类型标志_, /*isFlags*/ 为真);
    }

    导出 函数 formatObjectFlags(标志: 对象标志_): 文字 {
        返回 formatEnum(标志, (<任意>ts).对象标志_, /*isFlags*/ 为真);
    }

    /**
     * Creates a new 文本范围_ from the provided 首位 and 尾位.
     *
     * @param 首位 The 开始 position.
     * @param 尾位 The 尾位 position.
     */
    导出 函数 createRange(首位: 数字, 尾位: 数字): 文本范围_ {
        返回 { 首位, 尾位 };
    }

    /**
     * Creates a new 文本范围_ from a provided range with a new 尾位 position.
     *
     * @param range A 文本范围_.
     * @param 尾位 The new 尾位 position.
     */
    导出 函数 moveRangeEnd(range: 文本范围_, 尾位: 数字): 文本范围_ {
        返回 createRange(range.首位, 尾位);
    }

    /**
     * Creates a new 文本范围_ from a provided range with a new 开始 position.
     *
     * @param range A 文本范围_.
     * @param 首位 The new Start position.
     */
    导出 函数 moveRangePos(range: 文本范围_, 首位: 数字): 文本范围_ {
        返回 createRange(首位, range.尾位);
    }

    /**
     * Determines whether a 文本范围_ has the same 开始 and 尾位 positions.
     *
     * @param range A 文本范围_.
     */
    导出 函数 是折叠范围(range: 文本范围_) {
        返回 range.首位 === range.尾位;
    }

    /**
     * Creates a new 文本范围_ for a 令牌 at the provides 开始 position.
     *
     * @param 首位 The 开始 position.
     * @param 令牌 The 令牌.
     */
    导出 函数 createTokenRange(首位: 数字, 令牌: 语法_): 文本范围_ {
        返回 createRange(首位, 首位 + 令牌转为文字(令牌)!.长度);
    }

    导出 函数 rangeIsOnSingleLine(range: 文本范围_, 源文件: 源文件_) {
        返回 rangeStartIsOnSameLineAsRangeEnd(range, range, 源文件);
    }

    导出 函数 rangeStartPositionsAreOnSameLine(range1: 文本范围_, range2: 文本范围_, 源文件: 源文件_) {
        返回 positionsAreOnSameLine(getStartPositionOfRange(range1, 源文件), getStartPositionOfRange(range2, 源文件), 源文件);
    }

    导出 函数 rangeEndPositionsAreOnSameLine(range1: 文本范围_, range2: 文本范围_, 源文件: 源文件_) {
        返回 positionsAreOnSameLine(range1.尾位, range2.尾位, 源文件);
    }

    导出 函数 rangeStartIsOnSameLineAsRangeEnd(range1: 文本范围_, range2: 文本范围_, 源文件: 源文件_) {
        返回 positionsAreOnSameLine(getStartPositionOfRange(range1, 源文件), range2.尾位, 源文件);
    }

    导出 函数 rangeEndIsOnSameLineAsRangeStart(range1: 文本范围_, range2: 文本范围_, 源文件: 源文件_) {
        返回 positionsAreOnSameLine(range1.尾位, getStartPositionOfRange(range2, 源文件), 源文件);
    }

    导出 函数 positionsAreOnSameLine(pos1: 数字, pos2: 数字, 源文件: 源文件_) {
        返回 pos1 === pos2 ||
            getLineOfLocalPosition(源文件, pos1) === getLineOfLocalPosition(源文件, pos2);
    }

    导出 函数 getStartPositionOfRange(range: 文本范围_, 源文件: 源文件_) {
        返回 位置是合成的(range.首位) ? -1 : 跳过杂项(源文件.文本, range.首位);
    }

    导出 函数 是设置监控(选项: 编译选项_) {
        // Firefox has Object.prototype.watch
        返回 选项.监控 && 实例_.原型.具有本身属性.调用(选项, '监控');
    }

    导出 函数 getCheckFlags(符号: 符号_): CheckFlags {
        返回 符号.标志 & 符号标志_.临时的_ ? (<临时符号_>符号).checkFlags : 0;
    }

    导出 函数 getDeclarationModifierFlagsFromSymbol(s: 符号_): 修饰符组_ {
        如果 (s.值声明) {
            常量 标志 = 获取组合修饰符标志(s.值声明);
            返回  标志 & ~修饰符组_.AccessibilityModifier;
        }
        如果 (getCheckFlags(s) & CheckFlags.Synthetic) {
            常量 checkFlags = (<临时符号_>s).checkFlags;
            常量 accessModifier = checkFlags & CheckFlags.ContainsPrivate ? 修饰符组_.Private :
                checkFlags & CheckFlags.ContainsPublic ? 修饰符组_.Public :
                    修饰符组_.Protected;
            常量 staticModifier = checkFlags & CheckFlags.ContainsStatic ? 修饰符组_.Static : 0;
            返回 accessModifier | staticModifier;
        }
        返回 0;
    }

    导出 函数 skipAlias(符号: 符号_, checker: 类型检查_) {
        返回 符号.标志 & 符号标志_.别名_ ? checker.getAliasedSymbol(符号) : 符号;
    }

    /** See 注释 on `declareModuleMember` in `binder.ts`. */
    导出 函数 getCombinedLocalAndExportSymbolFlags(符号: 符号_): 符号标志_ {
        返回 符号.导出符号 ? 符号.导出符号.标志 | 符号.标志 : 符号.标志;
    }

    导出 函数 isWriteOnlyAccess(n: 节点_) {
        返回 accessKind(n) === AccessKind.Write;
    }

    导出 函数 isWriteAccess(n: 节点_) {
        返回 accessKind(n) !== AccessKind.Read;
    }

    常量 枚举 AccessKind {
        /** Only reads from a variable. */
        Read,
        /** Only writes to a variable without using the 结果. E.g.: `x++;`. */
        Write,
        /** Writes to a variable and uses the 结果 as an 表达式. E.g.: `f(x++);`. */
        ReadWrite
    }
    函数 accessKind(n: 节点_): AccessKind {
        常量 { 父节点 } = n;
        如果 (!父节点) 返回 AccessKind.Read;

        假如 (父节点.种类) {
            若是 语法_.后缀一元表达式_:
            若是 语法_.前缀一元表达式_:
                常量 { 运算符 } = 父节点 转为 前缀一元表达式_ | 后缀一元表达式_;
                返回 运算符 === 语法_.双加号_ || 运算符 === 语法_.双减号_ ? writeOrReadWrite() : AccessKind.Read;
            若是 语法_.二元表达式_:
                常量 { 左侧, 运算符令牌 } = 父节点 转为 二元表达式_;
                返回 左侧 === n && 是赋值运算符(运算符令牌.种类) ? writeOrReadWrite() : AccessKind.Read;
            若是 语法_.属性访问表达式_:
                返回 (父节点 转为 属性访问表达式_).名称 !== n ? AccessKind.Read : accessKind(父节点);
            默认:
                返回 AccessKind.Read;
        }

        函数 writeOrReadWrite(): AccessKind {
            // If grandparent is not an 表达式语句_, this is used as an 表达式 in addition to having a side effect.
            返回 父节点.父节点 && 父节点.父节点.种类 === 语法_.表达式语句_ ? AccessKind.Write : AccessKind.ReadWrite;
        }
    }

    导出 函数 比较数据对象(dst: 任意, src: 任意): 真假 {
        如果 (!dst || !src || 实例_.键组(dst).长度 !== 实例_.键组(src).长度) {
            返回 为假;
        }

        循环 (常量 e 位于 dst) {
            如果 (类为 dst[e] === 'object') {
                如果 (!比较数据对象(dst[e], src[e])) {
                    返回 为假;
                }
            }
            否则 如果 (类为 dst[e] !== '函数') {
                如果 (dst[e] !== src[e]) {
                    返回 为假;
                }
            }
        }
        返回 为真;
    }

    /**
     * clears already present map by calling onDeleteExistingValue callback before deleting that key/value
     */
    导出 函数 清除词典<T>(map: 词典_<T>, onDeleteValue: (valueInMap: T, key: 文字) => 无值) {
        // Remove all
        map.循环执行(onDeleteValue);
        map.清除();
    }

    导出 接口 多维词典选项_<T, U> {
        创建新值(key: 文字, valueInNewMap: U): T;
        正在删除值(existingValue: T, key: 文字): 无值;

        /**
         * If present this is called with the key when there is value for that key both in new map as well as existing map provided
         * Caller can then decide to update or remove this key.
         * If the key is removed, caller will get callback of createNewValue for that key.
         * If this callback is not provided, the value of such keys is not updated.
         */
        正在现有值?(existingValue: T, valueInNewMap: U, key: 文字): 无值;
    }

    /**
     * Mutates the map with newMap such that keys in map will be same as newMap.
     */
    导出 函数 多维词典<T, U>(map: 词典_<T>, newMap: 词典_<U>, options: 多维词典选项_<T, U>) {
        常量 { 创建新值: createNewValue, 正在删除值: onDeleteValue, 正在现有值: onExistingValue } = options;
        // Needs update
        map.循环执行((existingValue, key) => {
            常量 valueInNewMap = newMap.获取(key);
            // Not present any more in new map, remove it
            如果 (valueInNewMap === 未定) {
                map.删除(key);
                onDeleteValue(existingValue, key);
            }
            // If present notify about existing values
            否则 如果 (onExistingValue) {
                onExistingValue(existingValue, valueInNewMap, key);
            }
        });

        // Add new values that are not already present
        newMap.循环执行((valueInNewMap, key) => {
            如果 (!map.具有(key)) {
                // New values
                map.设置(key, createNewValue(key, valueInNewMap));
            }
        });
    }

    /** Calls `callback` on `directory` and every ancestor directory it has, returning the 首个 defined 结果. */
    导出 函数 循环每个祖先目录<T>(目录: 文字, cb: (目录: 文字) => T | 未定): T | 未定 {
        判断 (为真) {
            常量 结果 = cb(目录);
            如果 (结果 !== 未定) {
                返回 结果;
            }

            常量 父路径 = 获取目录路径(目录);
            如果 (父路径 === 目录) {
                返回 未定;
            }

            目录 = 父路径;
        }
    }


    导出 函数 getObjectFlags(类型: 类型_): 对象标志_ {
        返回 类型.标志 & 类型标志_.对象_ ? (<对象类型_>类型).对象标志 : 0;
    }

    导出 函数 typeHasCallOrConstructSignatures(类型: 类型_, checker: 类型检查_) {
        返回 checker.getSignaturesOfType(类型, 签名种类_.Call).长度 !== 0 || checker.getSignaturesOfType(类型, 签名种类_.Construct).长度 !== 0;
    }

    导出 函数 forSomeAncestorDirectory(directory: 文字, callback: (directory: 文字) => 真假): 真假 {
        返回 !!循环每个祖先目录(directory, d => callback(d) ? 为真 : 未定);
    }

    导出 函数 显示模块名称({ 模块名 }: 导入从声明_): 文字 {
        // 这个是 模块路径 // 不是包名
        返回 模块名.文本;
    }
}

名域 ts {
    导出 函数 获取默认支持库文件名(): 文字 {
        // lib_win_amd86.dll
        // 'lib_' + 选项.系统 + '_' + 选项.平台 + '.dll'
        // TODO
        返回 'lib.d.k';
    }

    导出 函数 文本跨度首位(跨度: 文本跨度_) {
        返回 跨度.开始 + 跨度.长度;
    }

    导出 函数 textSpanIsEmpty(跨度: 文本跨度_) {
        返回 跨度.长度 === 0;
    }

    导出 函数 textSpanContainsPosition(跨度: 文本跨度_, position: 数字) {
        返回 position >= 跨度.开始 && position < 文本跨度首位(跨度);
    }

    // Returns true if '跨度' contains 'other'.
    导出 函数 textSpanContainsTextSpan(跨度: 文本跨度_, other: 文本跨度_) {
        返回 other.开始 >= 跨度.开始 && 文本跨度首位(other) <= 文本跨度首位(跨度);
    }

    导出 函数 textSpanOverlapsWith(跨度: 文本跨度_, other: 文本跨度_) {
        返回 textSpanOverlap(跨度, other) !== 未定;
    }

    导出 函数 textSpanOverlap(span1: 文本跨度_, span2: 文本跨度_) {
        常量 overlap = textSpanIntersection(span1, span2);
        返回 overlap && overlap.长度 === 0 ? 未定 : overlap;
    }

    导出 函数 textSpanIntersectsWithTextSpan(跨度: 文本跨度_, other: 文本跨度_) {
        返回 decodedTextSpanIntersectsWith(跨度.开始, 跨度.长度, other.开始, other.长度);
    }

    导出 函数 textSpanIntersectsWith(跨度: 文本跨度_, 开始: 数字, length: 数字) {
        返回 decodedTextSpanIntersectsWith(跨度.开始, 跨度.长度, 开始, length);
    }

    导出 函数 decodedTextSpanIntersectsWith(start1: 数字, length1: 数字, start2: 数字, length2: 数字) {
        常量 end1 = start1 + length1;
        常量 end2 = start2 + length2;
        返回 start2 <= end1 && end2 >= start1;
    }

    导出 函数 textSpanIntersectsWithPosition(跨度: 文本跨度_, position: 数字) {
        返回 position <= 文本跨度首位(跨度) && position >= 跨度.开始;
    }

    导出 函数 textSpanIntersection(span1: 文本跨度_, span2: 文本跨度_) {
        常量 开始 = 数学_.max(span1.开始, span2.开始);
        常量 尾位 = 数学_.min(文本跨度首位(span1), 文本跨度首位(span2));
        返回 开始 <= 尾位 ? 从边界创建文本范围(开始, 尾位) : 未定;
    }

    导出 函数 创建文本跨度(开始: 数字, length: 数字): 文本跨度_ {
        如果 (开始 < 0) {
            抛出 新建 错误_('开始 < 0');
        }
        如果 (length < 0) {
            抛出 新建 错误_('length < 0');
        }

        返回 { 开始, 长度: length };
    }

    导出 函数 从边界创建文本范围(开始: 数字, 尾位: 数字) {
        返回 创建文本跨度(开始, 尾位 - 开始);
    }

    导出 函数 文本改变范围新跨度(范围: 文本改变范围_) {
        返回 创建文本跨度(范围.跨度.开始, 范围.新长度);
    }

    导出 函数 文本改变范围是未变(范围: 文本改变范围_) {
        返回 textSpanIsEmpty(范围.跨度) && 范围.新长度 === 0;
    }

    导出 函数 创建文本改变范围(跨度: 文本跨度_, 新长度: 数字): 文本改变范围_ {
        如果 (新长度 < 0) {
            抛出 新建 错误_('新长度 < 0');
        }

        返回 { 跨度, 新长度 };
    }

    导出 变量 unchangedTextChangeRange = 创建文本改变范围(创建文本跨度(0, 0), 0);

    /**
     * Called to merge all the changes that occurred across several versions of a script snapshot
     * into a single change.  i.e. if a user keeps making successive edits to a script we will
     * have a 文本 change from V1 to V2, V2 to V3, ..., Vn.
     *
     * This 函数 will then merge those changes into a single change range valid between V1 and
     * Vn.
     */
    导出 函数 collapseTextChangeRangesAcrossMultipleVersions(changes: 只读数组_<文本改变范围_>): 文本改变范围_ {
        如果 (changes.长度 === 0) {
            返回 unchangedTextChangeRange;
        }

        如果 (changes.长度 === 1) {
            返回 changes[0];
        }

        // We change from talking about { { oldStart, oldLength }, 新长度 } to { oldStart, oldEnd, newEnd }
        // as it makes things much easier to reason about.
        常量 change0 = changes[0];

        变量 oldStartN = change0.跨度.开始;
        变量 oldEndN = 文本跨度首位(change0.跨度);
        变量 newEndN = oldStartN + change0.新长度;

        循环 (变量 i = 1; i < changes.长度; i++) {
            常量 nextChange = changes[i];

            // Consider the following case:
            // i.e. two edits.  The 首个 represents the 文本 change range { { 10, 50 }, 30 }.  i.e. The 跨度 starting
            // at 10, with length 50 is reduced to length 30.  The second represents the 文本 change range { { 30, 30 }, 40 }.
            // i.e. the 跨度 starting at 30 with length 30 is increased to length 40.
            //
            //      0         10        20        30        40        50        60        70        80        90        100
            //      -------------------------------------------------------------------------------------------------------
            //                |                                                 /
            //                |                                            /----
            //  T1            |                                       /----
            //                |                                  /----
            //                |                             /----
            //      -------------------------------------------------------------------------------------------------------
            //                                     |                            \
            //                                     |                               \
            //   T2                                |                                 \
            //                                     |                                   \
            //                                     |                                      \
            //      -------------------------------------------------------------------------------------------------------
            //
            // Merging these turns out to not be too difficult.  First, determining the new 开始 of the change is trivial
            // it's just the min of the old and new starts.  i.e.:
            //
            //      0         10        20        30        40        50        60        70        80        90        100
            //      ------------------------------------------------------------*------------------------------------------
            //                |                                                 /
            //                |                                            /----
            //  T1            |                                       /----
            //                |                                  /----
            //                |                             /----
            //      ----------------------------------------$-------------------$------------------------------------------
            //                .                    |                            \
            //                .                    |                               \
            //   T2           .                    |                                 \
            //                .                    |                                   \
            //                .                    |                                      \
            //      ----------------------------------------------------------------------*--------------------------------
            //
            // (Note the dots represent the newly inferred 开始.
            // Determining the new and old 尾位 is also pretty simple.  Basically it boils down to paying attention to the
            // absolute positions at the asterisks, and the relative change between the dollar signs. Basically, we see
            // which if the two $'s precedes the other, and we move that one forward until they line up.  in this case that
            // means:
            //
            //      0         10        20        30        40        50        60        70        80        90        100
            //      --------------------------------------------------------------------------------*----------------------
            //                |                                                                     /
            //                |                                                                /----
            //  T1            |                                                           /----
            //                |                                                      /----
            //                |                                                 /----
            //      ------------------------------------------------------------$------------------------------------------
            //                .                    |                            \
            //                .                    |                               \
            //   T2           .                    |                                 \
            //                .                    |                                   \
            //                .                    |                                      \
            //      ----------------------------------------------------------------------*--------------------------------
            //
            // In other words (in this case), we're recognizing that the second edit happened after where the 首个 edit
            // ended with a 增量量 of 20 characters (60 - 40).  Thus, if we go back in time to where the 首个 edit started
            // that's the same as if we started at char 80 instead of 60.
            //
            // As it so happens, the same logic applies if the second edit precedes the 首个 edit.  In that case rather
            // than pushing the 首个 edit forward to match the second, we'll push the second edit forward to match the
            // 首个.
            //
            // In this case that means we have { oldStart: 10, oldEnd: 80, newEnd: 70 } or, in 文本改变范围_
            // semantics: { { 开始: 10, length: 70 }, 新长度: 60 }
            //
            // The math then works out as follows.
            // If we have { oldStart1, oldEnd1, newEnd1 } and { oldStart2, oldEnd2, newEnd2 } then we can compute the
            // final 结果 like so:
            //
            // {
            //      oldStart3: Min(oldStart1, oldStart2),
            //      oldEnd3: Max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1)),
            //      newEnd3: Max(newEnd2, newEnd2 + (newEnd1 - oldEnd2))
            // }

            常量 oldStart1 = oldStartN;
            常量 oldEnd1 = oldEndN;
            常量 newEnd1 = newEndN;

            常量 oldStart2 = nextChange.跨度.开始;
            常量 oldEnd2 = 文本跨度首位(nextChange.跨度);
            常量 newEnd2 = oldStart2 + nextChange.新长度;

            oldStartN = 数学_.min(oldStart1, oldStart2);
            oldEndN = 数学_.max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1));
            newEndN = 数学_.max(newEnd2, newEnd2 + (newEnd1 - oldEnd2));
        }

        返回 创建文本改变范围(从边界创建文本范围(oldStartN, oldEndN), /*新长度*/ newEndN - oldStartN);
    }

    导出 函数 获取组合修饰符标志(n: 节点_): 修饰符组_ {
        变量 标志 = getModifierFlags(n)!;
        如果 (n.种类 === 语法_.变量声明_) {
            n = n.父节点;
        }

        如果 (n && n.种类 === 语法_.变量语句_) {
            标志 |= getModifierFlags(n)!;
            n = n.父节点;
        }

        如果 (n && n.种类 === 语法_.变量语句_) {
            标志 |= getModifierFlags(n)!;
        }

        返回 标志;
    }

    // Returns the n 标志 for this n and all relevant 父节点 nodes.  This is done so that
    // nodes like variable 声明组 and binding 元素组 can returned a view of their 标志
    // that includes the 修饰符 from their container.  i.e. 标志 like 导出/declare aren't
    // stored on the variable declaration directly, but on the containing variable 语句
    // (if it has one).  Similarly, 标志 for 变量/const are store on the variable declaration
    // 列表.  By calling this 函数, all those 标志 are combined so that the client can treat
    // the n as if it actually had those 标志.
    导出 函数 获取组合节点标志(n: 节点_): 节点标志_ {

        变量 标志 = n.标志;
        如果 (n.种类 === 语法_.变量声明_) {
            n = n.父节点;
        }

        如果 (n && n.种类 === 语法_.变量语句_) {
            标志 |= n.标志;
        }

        返回 标志;
    }

    /**
     * Checks to see if the locale is in the appropriate format,
     * and if it is, attempts to set the appropriate language.
     */
    导出 函数 验证区域设置和设置语言(
        locale: 文字,
        sys: { 获取正执行的文件路径(): 文字, 解析路径(路径: 文字): 文字, 文件存在(文件名: 文字): 真假, 读文件(文件名: 文字): 文字 | 未定 },
        errors: 诊断_[]) {
        常量 matchResult = /^([a-z]+)([_\-]([a-z]+))?$/.执行代码(locale.转为小写());

        如果 (!matchResult) {
            如果 (errors) {
                errors.压入(创建编译器诊断(诊断信息_.Locale_must_be_of_the_form_language_or_language_territory_For_example_0_or_1, 'en', 'ja-jp'));
            }
            返回;
        }

        常量 language = matchResult[1];
        常量 territory = matchResult[3];

        // First try the entire locale, then fall back to just language if that's all we have.
        // Either ways do not fail, and fallback to the English diagnostic strings.
        如果 (!trySetLanguageAndTerritory(language, territory, errors)) {
            trySetLanguageAndTerritory(language, /*territory*/ 未定!, errors);
        }

        // Set the UI locale for string collation
        setUILocale(locale);

        函数 trySetLanguageAndTerritory(language: 文字, territory: 文字, errors?: 诊断_[]): 真假 {
            常量 compilerFilePath = 正规化路径(sys.获取正执行的文件路径());
            常量 containingDirectoryPath = 获取目录路径(compilerFilePath);

            变量 filePath = 合并路径(containingDirectoryPath, language);

            如果 (territory) {
                filePath = filePath + '-' + territory;
            }

            filePath = sys.解析路径(合并路径(filePath, 'diagnosticMessages.generated.json'));

            如果 (!sys.文件存在(filePath)) {
                返回 为假;
            }

            // TODO: Add codePage support for readFile?
            变量 fileContents = '';
            尝试 {
                fileContents = sys.读文件(filePath)!;
            }
            捕获 (e) {
                如果 (errors) {
                    errors.压入(创建编译器诊断(诊断信息_.Unable_to_open_file_0, filePath));
                }
                返回 为假;
            }
            尝试 {
                // tslint:disable-next-line no-unnecessary-qualifier (making clear this is a global mutation!)
                ts.本地化诊断信息 = JSON.解析(fileContents);
            }
            捕获 (e) {
                如果 (errors) {
                    errors.压入(创建编译器诊断(诊断信息_.Corrupted_locale_file_0, filePath));
                }
                返回 为假;
            }

            返回 为真;
        }
    }

    导出 函数 获取原始节点(n: 节点_): 节点_;
    导出 函数 获取原始节点<T 扩展 节点_>(n: 节点_, nodeTest: (n: 节点_) => n 作为 T): T;
    导出 函数 获取原始节点(n: 节点_, nodeTest?: (n: 节点_) => 真假): 节点_ | 未定 {
        如果 (n) {
            判断 (n.原始 !== 未定) {
                n = n.原始;
            }
        }

        返回 !nodeTest || nodeTest(n) ? n : 未定;
    }

    /**
     * Gets a value indicating whether a n originated in the parse tree.
     *
     * @param n The n to test.
     */
    导出 函数 isParseTreeNode(n: 节点_): 真假 {
        返回 (n.标志 & 节点标志_.合成的_) === 0;
    }

    /**
     * Gets the 原始 parse tree n for a n.
     *
     * @param n The 原始 n.
     * @returns The 原始 parse tree n if found; otherwise, 未定.
     */
    导出 函数 获取解析树节点(n: 节点_): 节点_;

    /**
     * Gets the 原始 parse tree n for a n.
     *
     * @param n The 原始 n.
     * @param nodeTest A callback used to ensure the correct 类型 of parse tree n is returned.
     * @returns The 原始 parse tree n if found; otherwise, 未定.
     */
    导出 函数 获取解析树节点<T 扩展 节点_>(n: 节点_, nodeTest?: (n: 节点_) => n 作为 T): T;
    导出 函数 获取解析树节点(n: 节点_, nodeTest?: (n: 节点_) => 真假): 节点_ | 未定 {
        如果 (n === 未定 || isParseTreeNode(n)) {
            返回 n;
        }

        n = 获取原始节点(n);

        如果 (isParseTreeNode(n) && (!nodeTest || nodeTest(n))) {
            返回 n;
        }

        返回 未定;
    }

    /**
     * Remove extra underscore from escaped 标识符 文本 内容.
     *
     * @param 标识符 The escaped 标识符 文本.
     * @returns The unescaped 标识符 文本.
     */
    导出 函数 unescapeLeadingUnderscores(标识符: 文字): 文字 {
        常量 id = 标识符 转为 文字;
        返回 id.长度 >= 3 && id.字符代码在(0) === 字符_._ && id.字符代码在(1) === 字符_._ && id.字符代码在(2) === 字符_._ ? id.子文本(1) : id;
    }

    导出 函数 idText(标识符: 标识符_): 文字 {
        返回 标识符.文本;
    }
    导出 函数 symbolName(符号: 符号_): 文字 {
        返回 符号.名称;
    }

    /**
     * Remove extra underscore from escaped 标识符 文本 内容.
     * @deprecated Use `id.文本` for the unescaped 文本.
     * @param 标识符 The escaped 标识符 文本.
     * @returns The unescaped 标识符 文本.
     */
    导出 函数 unescapeIdentifier(id: 文字): 文字 {
        返回 id;
    }

    /** @internal */
    导出 函数 是名称声明(n: 节点_): n 作为 名称声明_ {
        返回 !!(n 转为 名称声明_).名称; // A '名称' property should always be a 声明名称_.
    }

    导出 函数 获取声明名称(_声明: 声明_ | 表达式_): 名称声明_ | 未定 {
        如果 (!_声明) {
            返回 未定;
        }
        如果 (_声明.种类 === 语法_.标识符_) {
            返回 _声明 转为 标识符_;
        }
        返回 (_声明 转为 名称声明_).名称;
    }

    /**
     * Return true if the n has JSDoc 参数 标签组.
     *
     * @remarks Includes 参数 标签组 that are not directly on the n,
     * for example on a variable declaration whose 初始化 is a 函数 表达式.
     */
    导出 函数 hasJSDocParameterTags(n: 函数族声明_ | 签名声明_): 真假 {
        返回 !!getFirstJSDocTag(n, 语法_.JSDoc标签_);
    }



    /** Get all JSDoc 标签组 related to a n, including those on 父节点 nodes. */
    导出 函数 getJSDocTags(n: 节点_): 只读数组_<JSDoc标签_> | 未定 {
        变量 标签组 = (n 转为 JSDoc容器_).jsDoc缓存_;
        // If cache is 'null', that means we did the work of searching for JSDoc 标签组 and came up with nothing.
        如果 (标签组 === 未定) {
            (n 转为 JSDoc容器_).jsDoc缓存_ = 标签组 = 扁平词典(获取JSDoc注释和标签(n), j => isJSDoc(j) ? j.标签组 : j);
        }
        返回 标签组;
    }

    /** Get the 首个 JSDoc 标签 of a specified 种类, or 未定 if not present. */
    函数 getFirstJSDocTag(n: 节点_, 种类: 语法_): JSDoc标签_ | 未定 {
        常量 标签组 = getJSDocTags(n);
        返回 标签组 && 查找(标签组, doc => doc.种类 === 种类);
    }

    /** Gets all JSDoc 标签组 of a specified 种类, or 未定 if not present. */
    导出 函数 getAllJSDocTagsOfKind(n: 节点_, 种类: 语法_): 只读数组_<JSDoc标签_> | 未定 {
        常量 标签组 = getJSDocTags(n);
        返回 标签组 && 过滤器(标签组, doc => doc.种类 === 种类);
    }

}

// Simple n tests of the form `n.种类 === 语法_.Foo`.
名域 ts {
    // Literals
    导出 函数 是数字字面量(n: 节点_): n 作为 数字字面量_ {
        返回 n.种类 === 语法_.数字字面量_;
    }

    导出 函数 是文本字面量(n: 节点_): n 作为 文本字面量_ {
        返回 n.种类 === 语法_.文本字面量_;
    }

    导出 函数 isRegularExpressionLiteral(n: 节点_): n 作为 正则表达式字面量_ {
        返回 n.种类 === 语法_.正则表达式字面量_;
    }

    导出 函数 isNoSubstitutionTemplateLiteral(n: 节点_): n 作为 无替换模板字面量_ {
        返回 n.种类 === 语法_.无替换模板字面量_;
    }

    // Pseudo-literals

    导出 函数 isTemplateHead(n: 节点_): n 作为 模板头_ {
        返回 n.种类 === 语法_.模板头_;
    }

    导出 函数 isTemplateMiddle(n: 节点_): n 作为 模板中_ {
        返回 n.种类 === 语法_.模板中_;
    }

    导出 函数 isTemplateTail(n: 节点_): n 作为 模板尾_ {
        返回 n.种类 === 语法_.模板尾_;
    }

    导出 函数 是标识符(n: 节点_): n 作为 标识符_ {
        返回 n.种类 === 语法_.标识符_;
    }

    // Names

    导出 函数 isQualifiedName(n: 节点_): n 作为 限定名_ {
        返回 n.种类 === 语法_.限定名_;
    }

    // 签名_ 元素组


    导出 函数 isParameter(n: 节点_): n 作为 参数声明_ {
        返回 n.种类 === 语法_.参数_;
    }


    // TypeMember
    导出 函数 是结构声明(n: 节点_): n 作为 结构声明_ {
        返回 n.种类 === 语法_.结构声明_;
    }

    导出 函数 是枚举声明(n: 节点_): n 作为 枚举声明_ {
        返回 n.种类 === 语法_.枚举声明_;
    }
    
    导出 函数 是枚举成员声明(n: 节点_): n 作为 枚举成员声明_ {
        返回 n.种类 === 语法_.枚举成员声明_;
    }

    导出 函数 是属性声明(n: 节点_): n 作为 属性声明_ {
        返回 n.种类 === 语法_.属性声明_;
    }

    导出 函数 是结构字面量成员表达式(n: 节点_): n 作为 结构字面量成员表达式_ {
        返回 n.种类 === 语法_.结构字面量成员表达式_;
    }

    导出 函数 是接受者声明(n: 节点_): n 作为 接受者声明_{
        返回 n.种类 === 语法_.接受者声明_;
    }

    导出 函数 isMethodSignature(n: 节点_): n 作为 方法签名_ {
        返回 n.种类 === 语法_.方法签名_;
    }

    导出 函数 isMethodDeclaration(n: 节点_): n 作为 方法声明_ {
        返回 n.种类 === 语法_.方法声明_;
    }

    // 类型_
    导出 函数 isTypeReferenceNode(n: 节点_): n 作为 类型引用节点_ {
        返回 n.种类 === 语法_.类型引用_;
    }

    导出 函数 isArrayTypeNode(n: 节点_): n 作为 数组类型节点_ {
        返回 n.种类 === 语法_.数组类型_;
    }

    导出 函数 isUnionTypeNode(n: 节点_): n 作为 联合类型节点_ {
        返回 n.种类 === 语法_.联合类型_;
    }

    导出 函数 isParenthesizedTypeNode(n: 节点_): n 作为 括号类型节点_ {
        返回 n.种类 === 语法_.括号类型_;
    }

    // 表达式_

    导出 函数 是数组字面量表达式(n: 节点_): n 作为 数组字面量表达式_ {
        返回 n.种类 === 语法_.数组字面量表达式_;
    }

    导出 函数 isObjectLiteralExpression(n: 节点_): n 作为 结构字面量表达式_ {
        返回 n.种类 === 语法_.结构字面量表达式_;
    }

    导出 函数 是属性访问表达式(n: 节点_): n 作为 属性访问表达式_ {
        返回 n.种类 === 语法_.属性访问表达式_;
    }

    导出 函数 isElementAccessExpression(n: 节点_): n 作为 元素访问表达式_ {
        返回 n.种类 === 语法_.元素访问表达式_;
    }

    导出 函数 是调用表达式(n: 节点_): n 作为 调用表达式_ {
        返回 n.种类 === 语法_.调用表达式_;
    }

    导出 函数 isNewExpression(n: 节点_): n 作为 新建表达式_ {
        返回 n.种类 === 语法_.新建表达式_;
    }

    导出 函数 isTaggedTemplateExpression(n: 节点_): n 作为 标记模板表达式_ {
        返回 n.种类 === 语法_.标记模板表达式_;
    }

    导出 函数 isTypeAssertion(n: 节点_): n 作为 类型断言表达式_ {
        返回 n.种类 === 语法_.类型断言表达式_;
    }

    导出 函数 isParenthesizedExpression(n: 节点_): n 作为 括号表达式_ {
        返回 n.种类 === 语法_.括号表达式_;
    }

    导出 函数 跳过部分输出表达式(n: 表达式_): 表达式_;
    导出 函数 跳过部分输出表达式(n: 节点_): 节点_;
    导出 函数 跳过部分输出表达式(n: 节点_) {
        判断 (n.种类 === 语法_.部分输出表达式_) {
            n = (<部分输出语句_>n).表达式;
        }

        返回 n;
    }

    导出 函数 isFunctionExpression(n: 节点_): n 作为 函数表达式_ {
        返回 n.种类 === 语法_.函数表达式_;
    }

    导出 函数 isArrowFunction(n: 节点_): n 作为 箭头函数_ {
        返回 n.种类 === 语法_.箭头函数_;
    }

    导出 函数 isVoidExpression(n: 节点_): n 作为 无值表达式_ {
        返回 n.种类 === 语法_.无值表达式_;
    }

    导出 函数 isPrefixUnaryExpression(n: 节点_): n 作为 前缀一元表达式_ {
        返回 n.种类 === 语法_.前缀一元表达式_;
    }

    导出 函数 isPostfixUnaryExpression(n: 节点_): n 作为 后缀一元表达式_ {
        返回 n.种类 === 语法_.后缀一元表达式_;
    }

    导出 函数 是二元表达式(n: 节点_): n 作为 二元表达式_ {
        返回 n.种类 === 语法_.二元表达式_;
    }

    导出 函数 isConditionalExpression(n: 节点_): n 作为 条件表达式_ {
        返回 n.种类 === 语法_.条件表达式_;
    }

    导出 函数 isTemplateExpression(n: 节点_): n 作为 模板表达式_ {
        返回 n.种类 === 语法_.模板表达式_;
    }

    导出 函数 isOmittedExpression(n: 节点_): n 作为 省略表达式_ {
        返回 n.种类 === 语法_.省略表达式_;
    }


    导出 函数 isAsExpression(n: 节点_): n 作为 类型转换表达式_ {
        返回 n.种类 === 语法_.类型转换表达式_;
    }

    // Misc

    导出 函数 isTemplateSpan(n: 节点_): n 作为 模板跨度_ {
        返回 n.种类 === 语法_.模板跨度_;
    }


    // 块_

    导出 函数 是块(n: 节点_): n 作为 块_ {
        返回 n.种类 === 语法_.块_;
    }

    导出 函数 是变量语句(n: 节点_): n 作为 变量语句_ {
        返回 n.种类 === 语法_.变量语句_;
    }

    导出 函数 是简洁变量语句(n: 节点_): n 作为 简洁变量语句_ {
        返回 n.种类 === 语法_.简洁变量语句_;
    }

    导出 函数 isEmptyStatement(n: 节点_): n 作为 空语句_ {
        返回 n.种类 === 语法_.空语句_;
    }

    导出 函数 是表达式语句(n: 节点_): n 作为 表达式语句_ {
        返回 n.种类 === 语法_.表达式语句_;
    }

    导出 函数 isIfStatement(n: 节点_): n 作为 如果语句_ {
        返回 n.种类 === 语法_.如果语句_;
    }


    导出 函数 是循环语句(n: 节点_): n 作为 循环语句_ {
        返回 n.种类 === 语法_.循环语句_;
    }

    导出 函数 isContinueStatement(n: 节点_): n 作为 继续语句_ {
        返回 n.种类 === 语法_.继续语句_;
    }

    导出 函数 isBreakStatement(n: 节点_): n 作为 跳出语句_ {
        返回 n.种类 === 语法_.跳出语句_;
    }

    导出 函数 isBreakOrContinueStatement(n: 节点_): n 作为 跳出或继续语句_ {
        返回 n.种类 === 语法_.跳出语句_ || n.种类 === 语法_.继续语句_;
    }

    导出 函数 isReturnStatement(n: 节点_): n 作为 返回语句_ {
        返回 n.种类 === 语法_.返回语句_;
    }

    导出 函数 是假如语句(n: 节点_): n 作为 假如语句_ {
        返回 n.种类 === 语法_.假如语句_;
    }

    导出 函数 isLabeledStatement(n: 节点_): n 作为 标签语句_ {
        返回 n.种类 === 语法_.标签语句_;
    }


    导出 函数 isDebuggerStatement(n: 节点_): n 作为 调试语句_ {
        返回 n.种类 === 语法_.调试语句_;
    }

    导出 函数 是变量声明(n: 节点_): n 作为 变量声明_ {
        返回 n.种类 === 语法_.变量声明_;
    }

    导出 函数 是函数声明(n: 节点_): n 作为 函数声明_ {
        返回 n.种类 === 语法_.函数声明_;
    }

    导出 函数 是接口声明(n: 节点_): n 作为 接口声明_ {
        返回 n.种类 === 语法_.接口声明_;
    }
    
    导出 函数 是类型声明(n: 节点_): n 作为 类型声明_ {
        返回 n.种类 === 语法_.类型声明_;
    }

    导出 函数 是类型别名声明(n: 节点_): n 作为 类型别名声明_ {
        返回 n.种类 === 语法_.类型别名声明_;
    }

    导出 函数 isCaseBlock(n: 节点_): n 作为 若是块_ {
        返回 n.种类 === 语法_.若是块_;
    }

    导出 函数 是导入声明(n: 节点_): n 作为 导入声明_ {
        返回 n.种类 === 语法_.导入声明_;
    }

    导出 函数 是导入成员(n:节点_): n 作为 导入成员_{
        返回 n.种类=== 语法_.导入成员_
    }
    
    导出 函数 是导入从成员(n:节点_): n 作为 导入从成员_{
        返回 n.种类=== 语法_.导入从成员_
    }

    导出 函数 isMissingDeclaration(n: 节点_): n 作为 失踪声明_ {
        返回 n.种类 === 语法_.失踪声明_;
    }

    // Clauses

    导出 函数 isCaseClause(n: 节点_): n 作为 若是子句_ {
        返回 n.种类 === 语法_.若是子句_;
    }

    导出 函数 isDefaultClause(n: 节点_): n 作为 默认子句_ {
        返回 n.种类 === 语法_.默认子句_;
    }

    // Enum

    // Top-level nodes
    导出 函数 是源文件(n: 节点_): n 作为 源文件_ {
        返回 n.种类 === 语法_.源文件_;
    }

    导出 函数 isBundle(n: 节点_): n 作为 源文件集_ {
        返回 n.种类 === 语法_.源文件集_;
    }

    导出 函数 isJSDoc(n: 节点_): n 作为 JSDoc_ {
        返回 n.种类 === 语法_.JsDoc注释_;
    }

    导出 函数 是模块(n: 节点_): n 作为 模块_{
        返回  n.种类 === 语法_.模块_;
    }

}

// 节点_ tests
//
// All n tests in the following 列表 should *not* reference 父节点 pointers so that
// they may be used with transformations.
名域 ts {
    /* @internal */
    导出 函数 isSyntaxList(n: 节点_): n 作为 语法列表_ {
        返回 n.种类 === 语法_.语法列表_;
    }

    /* @internal */
    导出 函数 isNode(n: 节点_) {
        返回 是节点种类(n.种类);
    }

    /* @internal */
    导出 函数 是节点种类(种类: 语法_) {
        返回 种类 >= 语法_.首节点_;
    }

    /**
     * 真_ if n is of some 令牌 syntax 种类.
     * For example, this is true for an 如果_ but not for an 如果语句_.
     * Literals are considered tokens, except 模板字面量_, but does include 模板头_/Middle/Tail.
     */
    导出 函数 isToken(n: 节点_): 真假 {
        返回 n.种类 >= 语法_.首令牌_ && n.种类 <= 语法_.尾令牌_;
    }

    // 节点_ Arrays

    /* @internal */
    导出 函数 isNodeArray<T 扩展 节点_>(数组: 只读数组_<T>): 数组 作为 节点数组_<T> {
        返回 hasOwnProperty.调用(数组, '首位') && hasOwnProperty.调用(数组, '尾位');
    }

    // Literals

    /* @internal */
    导出 函数 是字面量种类(种类: 语法_): 真假 {
        返回 语法_.首字面量令牌_ <= 种类 && 种类 <= 语法_.尾字面量令牌_;
    }

    导出 函数 isLiteralExpression(n: 节点_): n 作为 字面量表达式_ {
        返回 是字面量种类(n.种类);
    }

    // Pseudo-literals

    /* @internal */
    导出 函数 是模板字面量种类(种类: 语法_): 真假 {
        返回 语法_.首模板令牌_ <= 种类 && 种类 <= 语法_.尾模板令牌_;
    }

    导出 函数 isTemplateMiddleOrTemplateTail(n: 节点_): n 作为 模板中_ | 模板尾_ {
        常量 种类 = n.种类;
        返回 种类 === 语法_.模板中_
            || 种类 === 语法_.模板尾_;
    }

    导出 函数 isStringTextContainingNode(n: 节点_) {
        返回 n.种类 === 语法_.文本字面量_ || 是模板字面量种类(n.种类);
    }

    // Identifiers

    /* @internal */
    导出 函数 isGeneratedIdentifier(n: 节点_): n 作为 自动标识符_ {
        // Using `>` here catches both `GeneratedIdentifierKind.无_` and `未定`.
        返回 是标识符(n) && (n.自动标志! & 自动的标识符标志_.KindMask) > 自动的标识符标志_.无_;
    }

    // Keywords
    导出 函数 是导出标识符(text: 文字): 真假 {
        返回 !!text && (text.字符代码在(0) === 字符_._)
    }

    导出 函数 isEntityName(n: 节点_): n 作为 实体名称_ {
        常量 种类 = n.种类;
        返回 种类 === 语法_.限定名_
            || 种类 === 语法_.标识符_;
    }

    // Functions

    导出 函数 是函数族(n: 节点_): n 作为 签名声明_ {
        返回 n && 是函数族种类(n.种类);
    }

    /* @internal */
    导出 函数 是函数族声明(n: 节点_): n 作为 函数族声明_ {
        返回 n && 是函数族声明种类(n.种类);
    }

    函数 是函数族声明种类(种类: 语法_): 真假 {
        假如 (种类) {
            若是 语法_.函数声明_:
            若是 语法_.方法声明_:
            若是 语法_.启动函数声明_:
            若是 语法_.初始函数声明_:
            若是 语法_.函数表达式_:
            若是 语法_.箭头函数_:
                返回 为真;
            默认:
                返回 为假;
        }
    }

    /* @internal */
    导出 函数 是函数族种类(种类: 语法_): 真假 {
        假如 (种类) {
            若是 语法_.方法签名_:
            若是 语法_.函数类型_:
            若是 语法_.创建函数签名_:
                返回 为真;
            默认:
                返回 是函数族声明种类(种类);
        }
    }

    导出 函数 是函数或模块块(n: 节点_): 真假 {
        返回 是源文件(n) || 是块(n) && 是函数族(n.父节点);
    }


    导出 函数 是方法声明(n: 节点_): n 作为 方法声明_  {
        返回 n.种类 === 语法_.方法声明_;
    }

    导出 函数 是方法签名(n: 节点_): n 作为 方法签名_  {
        返回 n.种类 === 语法_.方法签名_;
    }

    导出 函数 是程序声明(n: 节点_): n 作为 程序声明_{
        返回 n.种类 === 语法_.程序声明_
    }

    // 类型_
    函数 是类型节点种类(种类: 语法_) {
        返回 (种类 >= 语法_.首类型节点_ && 种类 <= 语法_.尾类型节点_)
            || 种类 === 语法_.通用_
            || 种类 === 语法_.整数_
            || 种类 === 语法_.真假_
            || 种类 === 语法_.文本_
            || 种类 === 语法_.无值_
            || 种类 === 语法_.未定_;
    }

    函数 是内置类型节点(种类: 语法_) {
        返回 种类 === 语法_.真假_
            || 种类 === 语法_.无值_
            || 种类 === 语法_.文本_
            || 种类 === 语法_.整数_
            || 种类 === 语法_.整数8_
            || 种类 === 语法_.整数16_
            || 种类 === 语法_.整数32_
            || 种类 === 语法_.整数64_
            || 种类 === 语法_.正整数_
            || 种类 === 语法_.正整数8_
            || 种类 === 语法_.正整数16_
            || 种类 === 语法_.正整数32_
            || 种类 === 语法_.正整数64_
            || 种类 === 语法_.小数_
            || 种类 === 语法_.小数32_
            || 种类 === 语法_.小数64_
            || 种类 === 语法_.复数64_
            || 种类 === 语法_.复数128_
            || 种类 === 语法_.指针_
            || 种类 === 语法_.字符_
            || 种类 === 语法_.字节_
    }

    导出 函数 获取命名的类型节点名称(n: 类型节点_): 文字 | 未定 {
        如果 (是指针类型节点(n)) {
            获取命名的类型节点名称(n.基础类型)
        }
        如果 (是类型引用节点(n) && 是标识符(n.类型名)) {
            返回 n.类型名.文本
        }
        如果 (是内置类型节点(n.种类)) {
            返回 令牌转为文字(n.种类)
        }
        返回 未定
    }

    /**
     * 节点_ test that determines whether a n is a valid 类型 n.
     * This differs from the `isPartOfTypeNode` 函数 which determines whether a n is *part*
     * of a 类型节点_.
     */
    导出 函数 是类型节点(n: 节点_): n 作为 类型节点_ {
        返回 是类型节点种类(n.种类);
    }

    导出 函数 是函数类型节点(n: 节点_): n 作为 函数类型节点_ {
        返回 n.种类 === 语法_.函数类型_;
    }

    // Binding patterns

    /* @internal */
    导出 函数 是赋值模式(n: 节点_): n 作为 赋值模式_ {
        常量 种类 = n.种类;
        返回 种类 === 语法_.数组字面量表达式_ || 种类 === 语法_.结构字面量表达式_;
    }

    /**
     * Determines whether the 绑定或赋值元素_ is a 绑定元素_-like declaration
     */
    /* @internal */
    导出 函数 是声明绑定元素(绑定元素: 绑定或赋值元素_): 绑定元素 作为 变量声明_ | 参数声明_  {
        假如 (绑定元素.种类) {
            若是 语法_.变量声明_:
            若是 语法_.参数_:
                返回 为真;
        }
        返回 为假;
    }

    /**
     * Determines whether a n is a 绑定或赋值模式_
     */
    /* @internal */
    导出 函数 是绑定或赋值模式(n: 绑定或赋值元素目标_): n 作为 绑定或赋值模式_ {
        返回 是结构绑定或赋值模式(n)
            || 是数组绑定模式(n);
    }

    /**
     * Determines whether a n is an 对象绑定或赋值模式_
     */
    /* @internal */
    导出 函数 是结构绑定或赋值模式(n: 绑定或赋值元素目标_): n 作为 结构绑定或赋值模式_ {
        假如 (n.种类) {
            若是 语法_.结构字面量表达式_:
                返回 为真;
        }

        返回 为假;
    }

    /**
     * Determines whether a n is an 数组绑定或赋值模式_
     */
    /* @internal */
    导出 函数 是数组绑定模式(n: 绑定或赋值元素目标_): n 作为 数组绑定或赋值模式_ {
        返回 n.种类 === 语法_.数组字面量表达式_;
    }

    // 表达式_

    导出 函数 是属性访问或限定名(n: 节点_): n 作为 属性访问表达式_ | 限定名_ {
        常量 种类 = n.种类;
        返回 种类 === 语法_.属性访问表达式_
            || 种类 === 语法_.限定名_;
    }

    导出 函数 是限定名(n: 节点_): n 作为  限定名_ {
        返回  n.种类 === 语法_.限定名_;
    }

    导出 函数 是调用表达式族(n: 节点_): n 作为 调用族表达式_ {
        假如 (n.种类) {
            若是 语法_.调用表达式_:
            若是 语法_.新建表达式_:
            若是 语法_.标记模板表达式_:
                返回 为真;
            默认:
                返回 为假;
        }
    }

    导出 函数 是调用或新建表达式(n: 节点_): n 作为 调用表达式_ | 新建表达式_ {
        返回 n.种类 === 语法_.调用表达式_ || n.种类 === 语法_.新建表达式_;
    }

    导出 函数 是模板字面量(n: 节点_): n 作为 模板字面量_ {
        常量 种类 = n.种类;
        返回 种类 === 语法_.模板表达式_
            || 种类 === 语法_.无替换模板字面量_;
    }

    /* @internal */
    导出 函数 是左侧表达式(n: 节点_): n 作为 左侧表达式_ {
        返回 是左侧表达式种类(跳过部分输出表达式(n).种类);
    }

    函数 是左侧表达式种类(种类: 语法_): 真假 {
        假如 (种类) {
            若是 语法_.属性访问表达式_:
            若是 语法_.元素访问表达式_:
            若是 语法_.新建表达式_:
            若是 语法_.调用表达式_:
            若是 语法_.标记模板表达式_:
            若是 语法_.括号表达式_:
            若是 语法_.函数表达式_:
            若是 语法_.箭头函数_:
            若是 语法_.标识符_:
            若是 语法_.模板表达式_:
            若是 语法_.数字字面量_:
            若是 语法_.文本字面量_:
            若是 语法_.取值表达式_:
            若是 语法_.取值表达式_:
                返回 为真;
            默认:
                返回 为假;
        }
    }

    /* @internal */
    导出 函数 是一元表达式(n: 节点_): n 作为 一元表达式_ {
        返回 是一元表达式种类(跳过部分输出表达式(n).种类);
    }

    函数 是一元表达式种类(种类: 语法_): 真假 {
        假如 (种类) {
            若是 语法_.前缀一元表达式_:
            若是 语法_.后缀一元表达式_:
            若是 语法_.无值表达式_:
            若是 语法_.类型断言表达式_:
                返回 为真;
            默认:
                返回 是左侧表达式种类(种类);
        }
    }

    /* @internal */
    导出 函数 是一样表达式包括写(表达式: 节点_): 表达式 作为 前缀一元表达式_ | 后缀一元表达式_ {
        假如 (表达式.种类) {
            若是 语法_.后缀一元表达式_:
                返回 为真;
            若是 语法_.前缀一元表达式_:
                返回 (<前缀一元表达式_>表达式).运算符 === 语法_.星号_||
                    (<前缀一元表达式_>表达式).运算符 === 语法_.且号_;
            默认:
                返回 为假;
        }
    }

    /* @internal */
    /**
     * Determines whether a n is an 表达式 based only on its 种类.
     * Use `isExpressionNode` if not in transforms.
     */
    导出 函数 是表达式(n: 节点_): n 作为 表达式_ {
        返回 是表达式种类(跳过部分输出表达式(n).种类);
    }

    函数 是表达式种类(种类: 语法_): 真假 {
        假如 (种类) {
            若是 语法_.条件表达式_:
            若是 语法_.箭头函数_:
            若是 语法_.二元表达式_:
            若是 语法_.展开元素_:
            若是 语法_.类型转换表达式_:
            若是 语法_.省略表达式_:
            若是 语法_.逗号列表表达式_:
            若是 语法_.部分输出表达式_:
                返回 为真;
            默认:
                返回 是一元表达式种类(种类);
        }
    }

    导出 函数 是断言表达式(n: 节点_): n 作为 断言表达式_ {
        常量 种类 = n.种类;
        返回 种类 === 语法_.类型断言表达式_
            || 种类 === 语法_.类型转换表达式_;
    }

    /* @internal */
    导出 函数 是部分输出表达式(n: 节点_): n 作为 部分输出语句_ {
        返回 n.种类 === 语法_.部分输出表达式_;
    }

    /* @internal */
    导出 函数 是不输出语句(n: 节点_): n 作为 不输出语句_ {
        返回 n.种类 === 语法_.不输出语句_;
    }

    /* @internal */
    导出 函数 是不输出或部分输出节点(n: 节点_): n 作为 不输出语句_ | 部分输出语句_ {
        返回 是不输出语句(n)
            || 是部分输出表达式(n);
    }

    // 语句_

    导出 函数 是迭代器语句(n: 节点_, 带标签语句: 为假): n 作为 迭代语句_;
    导出 函数 是迭代器语句(n: 节点_, 带标签语句: 真假): n 作为 迭代语句_ | 标签语句_;
    导出 函数 是迭代器语句(n: 节点_, 带标签语句: 真假): n 作为 迭代语句_ {
        假如 (n.种类) {
            若是 语法_.循环语句_:
            若是 语法_.循环属于语句_:
            若是 语法_.循环属于语句_:
                返回 为真;
            若是 语法_.标签语句_:
                返回 带标签语句 && 是迭代器语句((<标签语句_>n).语句, 带标签语句);
        }

        返回 为假;
    }

    /* @internal */
    导出 函数 是循环属于语句(n: 节点_): n 作为 循环属于语句_ {
        返回 n.种类 === 语法_.循环属于语句_;
    }

    导出 函数 是块范围声明(声明节点: 声明_) {
        返回  (获取组合节点标志(声明节点) & 节点标志_.块范围检测_) !== 0 
    }

    // Element

    /* @internal */
    导出 函数 是简洁体(n: 节点_): n 作为 简洁主体_ {
        返回 是块(n)
            || 是表达式(n);
    }

    /* @internal */
    导出 函数 是函数主体(n: 节点_): n 作为 函数主体_ {
        返回 是块(n);
    }

    /* @internal */
    导出 函数 是循环初始化(n: 节点_): n 作为 循环初始化_ {
        返回  是表达式(n);
    }

    函数 是声明种类(种类: 语法_) {
        返回 种类 === 语法_.箭头函数_
            || 种类 === 语法_.函数声明_
            || 种类 === 语法_.函数表达式_
            || 种类 === 语法_.接口声明_
            || 种类 === 语法_.方法声明_
            || 种类 === 语法_.方法签名_
            || 种类 === 语法_.参数_
            || 种类 === 语法_.属性声明_
            || 种类 === 语法_.类型别名声明_
            || 种类 === 语法_.变量声明_
            || 种类 === 语法_.类型参数声明_
            || 种类 === 语法_.结构声明_ 
            || 种类 === 语法_.枚举声明_
            || 种类 === 语法_.枚举成员声明_
            || 种类 === 语法_.导入从声明_
            || 种类 === 语法_.导入从成员_
            || 种类 === 语法_.类型声明_
            || 种类 === 语法_.结构字面量成员表达式_
    }

    函数 是声明语句种类(种类: 语法_) {
        返回  种类 === 语法_.函数声明_
        || 种类 === 语法_.失踪声明_
        || 种类 === 语法_.接口声明_
        || 种类 === 语法_.类型别名声明_
        || 种类 === 语法_.类型声明_
        || 种类 === 语法_.方法声明_
        || 种类 === 语法_.枚举声明_
        || 种类 === 语法_.导入声明_
        || 种类 === 语法_.导入从声明_
        || 种类 === 语法_.程序声明_
        || 种类 === 语法_.常量列表语句_
        || 种类 === 语法_.常量语句_
        || 种类 === 语法_.变量语句_
        || 种类 === 语法_.启动函数声明_
        || 种类 === 语法_.初始函数声明_        
        || 种类 === 语法_.创建函数签名_
        || 种类 === 语法_.方法签名_;
    }

    函数 是语句种类但不是声明种类(种类: 语法_) {
        返回 种类 === 语法_.跳出语句_
            || 种类 === 语法_.继续语句_
            || 种类 === 语法_.调试语句_
            || 种类 === 语法_.表达式语句_
            || 种类 === 语法_.空语句_
            || 种类 === 语法_.循环属于语句_
            || 种类 === 语法_.循环语句_
            || 种类 === 语法_.如果语句_
            || 种类 === 语法_.标签语句_
            || 种类 === 语法_.返回语句_
            || 种类 === 语法_.假如语句_
            || 种类 === 语法_.变量语句_
            || 种类 === 语法_.不输出语句_
            || 种类 === 语法_.声明结束标志_
            || 种类 === 语法_.合并声明标记_
    }

    /* @internal */
    导出 函数 是声明(n: 节点_): n 作为 名称声明_ {
        返回 是声明种类(n.种类);
    }

    /* @internal */
    导出 函数 是声明语句(n: 节点_): n 作为 声明语句_ {
        返回 是声明语句种类(n.种类);
    }

    /**
     * Determines whether the n is a 语句 that is not also a declaration
     */
    /* @internal */
    导出 函数 是语句但不是声明(n: 节点_): n 作为 语句_ {
        返回 是语句种类但不是声明种类(n.种类);
    }

    /* @internal */
    导出 函数 是语句(n: 节点_): n 作为 语句_ {
        常量 种类 = n.种类;
        返回 是语句种类但不是声明种类(种类)
            || 是声明语句种类(种类)
            || 是块语句(n);
    }

    函数 是块语句(n: 节点_): n 作为 块_ {
        如果 (n.种类 !== 语法_.块_) 返回 为假;
        返回 !是函数块(n);
    }

    // Module references

    /* @internal */
    导出 函数 是模块引用(n: 节点_): n 作为 模块引用_ {
        常量 种类 = n.种类;
        返回  种类 === 语法_.限定名_
            || 种类 === 语法_.标识符_;
    }

    // Clauses

    导出 函数 是若是或默认子句(n: 节点_): n 作为 若是或默认子句_ {
        常量 种类 = n.种类;
        返回 种类 === 语法_.若是子句_
            || 种类 === 语法_.默认子句_;
    }

    // JSDoc

    /** 真_ if n is of some JSDoc syntax 种类. */
    /* @internal */
    导出 函数 是JSDoc节点(n: 节点_): 真假 {
        返回 n.种类 >= 语法_.首JsDoc节点_ && n.种类 <= 语法_.尾JsDoc节点_;
    }

    /** 真_ if n is of a 种类 that may contain 注释 文本. */
    导出 函数 是JSDoc注释内容节点(n: 节点_): 真假 {
        返回 n.种类 === 语法_.JsDoc注释_ || 是JSDoc标签(n);
    }

    // TODO: determine what this does before making it public.
    /* @internal */
    导出 函数 是JSDoc标签(n: 节点_): 真假 {
        返回 n.种类 >= 语法_.首JsDoc标签节点_ && n.种类 <= 语法_.尾JsDoc标签节点_;
    }


    /** 真_ if has jsdoc nodes attached to it. */
    /* @internal */
    导出 函数 具有JSDoc节点组(n: 节点_): n 作为 有JSDoc_ {
        返回 !!(n 转为 JSDoc容器_).jsDoc && (n 转为 JSDoc容器_).jsDoc!.长度 > 0;
    }

    /** 真_ if has 类型 n attached to it. */
    /* @internal */
    导出 函数 具有类型(n: 节点_): n 作为 有类型_ {
        返回 !!(n 转为 有类型_).类型;
    }
    
    导出 函数 是类型引用节点(n: 节点_): n 作为 类型引用节点_ {
        返回 n.种类 === 语法_.类型引用_;
    }
    
    导出 函数 是变长参数类型节点(n: 节点_): n 作为 变长参数类型_{
        返回 n.种类 === 语法_.变长参数类型_;
    }

    导出 函数 是文本字面量族(n: 节点_): n 作为 字面量族_ {
        返回 n.种类 === 语法_.文本字面量_ || n.种类 === 语法_.无替换模板字面量_;
    }

    导出 函数 是取值表达式(n: 表达式_): n 作为 取值表达式_{
        返回  n.种类 === 语法_.取值表达式_
    }

    导出 函数  是指针类型节点(n: 节点_): n 作为 指针类型节点_{
        返回 n.种类 === 语法_.指针类型_

    }

    导出 函数 是取址表达式(n: 表达式_): n 作为 取址表达式_{
        返回  n.种类 === 语法_.取址表达式_
    }

    导出 函数 读Json(路径: 文字, 主机: 模块解析主机_): KlangJson_ {
        尝试 {
            常量 jsonText = 主机.读文件(路径);
            返回 jsonText ? JSON.解析(jsonText) : {};
        }
        捕获 (e) {
            // gracefully handle if readFile fails or returns not JSON
            返回 {};
        }
    }
}
名域 ts{
    
    导出 函数  getImmediatelyInvokedFunctionExpression(func: 节点_): 调用表达式_ | 未定 {
        如果 (func.种类 === 语法_.函数表达式_|| func.种类 === 语法_.箭头函数_) {
            变量 prev = func;
            变量 parent = func.父节点;
            判断 (parent.种类 === 语法_.括号表达式_) {
                prev = parent;
                parent = parent.父节点;
            }
            如果 (parent.种类 === 语法_.调用表达式_ && (parent 转为 调用表达式_).表达式 === prev) {
                返回 parent 转为 调用表达式_;
            }
        }
        返回 未定
    }

    导出 函数 是函数表达式或箭头函数(n: 节点_): n 作为 函数表达式_ | 箭头函数_ {
        返回 !!(n.种类 === 语法_.函数表达式_ || n.种类 === 语法_.箭头函数_)
    }

    导出 函数 getFunctionFlags(node: 签名声明_ | 未定) {
        如果 (!node) {
            返回 函数标志_.无效的;
        }
        变量 flags = 函数标志_.普通_;
        如果 (!(node 转为 函数族声明_).主体) {
            flags |= 函数标志_.无效的;
        }
        返回 flags;
    }

    导出 函数 getContainingFunction(node: 节点_): 签名声明_ | 未定 {
        返回 查找祖先(node.父节点, 是函数族);
    }

    
    导出 函数 skipOuterExpressions(node: 表达式_, kinds?: 外部表达式种类_): 表达式_;
    导出 函数 skipOuterExpressions(node: 节点_, kinds?: 外部表达式种类_): 节点_;
    导出 函数 skipOuterExpressions(node: 节点_, kinds = 外部表达式种类_.All) {
        变量 previousNode: 节点_;
        运行 {
            previousNode = node;
            如果 (kinds & 外部表达式种类_.Parentheses) {
                node = 跳过括号(node);
            }
        }
        判断 (previousNode !== node);

        返回 node;
    }

    导出 函数 getEffectiveTypeParameterDeclarations(node: 声明包括类型参数_): 只读数组_<类型参数声明_> {        
        如果 (node.类型参数) {
            返回 node.类型参数 ;
        }
        返回 空数组;
    }
}